【福建集训】背包(1.5/3)
A.大鱼吃小鱼
完全背包模板,但加上了一个限制。f[i]表示分数==i时花费时间的最小值,显然每次转移前判断一下当前分数够不够即可。注意dp顺序,外层是体积,内层是物品,这样就不用再对物品需求分数排个序再处理了
#include<bits/stdc++.h> using namespace std; #define re register #define fo1(l,r) for(re int i=l;i<=r;++i) #define fo2(l,r) for(re int j=l;j<=r;++j) #define fo3(l,r) for(re int k=l;k<=r;++k) #define fo4(l,r) for(re int tt=l;tt<=r;++tt) #define fo(l) for(re int i=h[l],go;i;i=x[i].last) #define inf 0x3f3f3f3f #define INF 0x7fffffffffffffff-2e9 #define LL long long #define itn int #define DB double inline LL read() { LL x=0,f=1;char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)) { x=(x<<3)+(x<<1)+ch-48; ch=getchar(); } return x*f; } const int N=1010; LL a[N],p[N],T[N],f[N]; inline LL max2(LL xx,LL yy) { return xx>yy?xx:yy; } inline LL min2(LL xx,LL yy) { return xx<yy?xx:yy; } int main() { freopen("frenzy.in","r",stdin); freopen("frenzy.out","w",stdout); int t=read(); fo4(1,t) { int n=read(),k=read(); fo1(1,n) { a[i]=read(); p[i]=read(); T[i]=read(); } fo1(1,k) f[i]=INF; LL ANS=INF; f[0]=0; //f[i]表示分数==i时花费时间的最小值 fo1(0,k)//i { fo2(1,n)//j { if(i>=a[j]) { if(i+p[j]>=k) { ANS=min2(ANS,f[i]+T[j]); } else { f[i+p[j]]=min2(f[i+p[j]],f[i]+T[j]); } } } } printf("%lld\n",ANS); } fclose(stdin); fclose(stdout); return 0; }
B.子集统计

这条结论的充分性是显然的,用反证法易证。
这个结论实际上告诉了我们转移的方式和条件,显然应以ai划分阶段
f[i][j]表示前i个数字和为j时的方案总数
对a排序后用刷表法转移
当j+1>=a[i+1]时进行转移f[i+1][j+a[i+1]]+=f[i][j]
正常定义会爆空间,事实上我们发现j+1>=a[i+1]而a[i+1]<=5000,所以当j超过5000时不妨将其改为5000,其转移不受影响

浙公网安备 33010602011771号