传说中的完全背包问题!!!
首先给大家看一下我的结构体,不然后期程序看不懂:
struct tree { int t,c,p; }; tree a[10001]; int s[10001][1001];
t是耗费时间,c是美学值,p是次数
其实本题跟普通的01背包差别并不大。 只不过需要加一个像下面一样的判断:
for(k=1;k<=x;++k) s[i][j]=max(s[i][j],s[i][j-a[i].t*k]+a[i].c*k);
其实就是将1到最多次数的所有情况遍历一遍。s是我储存DP结果的数组,而a就是储存每棵树的数据的数据,而x就是每棵树最多能看几次。
这时候你们是不是想问:那么无限次的怎么办呢? 其实也只需要一个判断,因为看得再多次,也不可能超出题目给出的时间限制。 也就是说每棵树都有一个最大观赏次数,而它就是极限次数:(总时间/当前树观赏一次的时间)和(题目给出次数)中小的那个
x=min(a[i].p,j/a[i].t);
但其实可以在节约一下空间,也就是DP可以重复利用空间,使空间的利用率最大。 从而就变成了这样:
for(i=1;i<=n;++i) { for(j=t;j>=a[i].t;--j) { x=min(a[i].p,j/a[i].t); for(k=1;k<=x;++k) s[j]=max(s[j],s[j-a[i].t*k]+a[i].c*k); } }
记得要倒序DP,因为前面的DP结果会影响后面的,如果先替换了,就会导致DP结果错误。
但是,我们发现,其实for(k=1;k<=x;++k)一直是在与自己进行DP,也就是说,只要我们略微修改一下状态转移方程,就可以将3重循环变为2重循环:
for(i=1;i<=n;++i) { if(a[i].p==0) for(j=a[i].t;j<=t;++j) { s[j]=max(s[j],s[j-a[i].t]+a[i].c); } else for(j=t;j>=a[i].t;--j) { for(k=1;k<=a[i].p;++k) s[j]=max(s[j],s[j-a[i].t*k]+a[i].c*k); } }
只需判断其是否是无限就行了。
所以将上述结构拼凑在一起就可以AC了。
2018-03-21