Cow Exhibition POJ - 2184
考察:01背包
完全不会...是看大佬的题解.
自己空想了几个小时,还是要动笔啊!!!
思路:
这里有两个属性值,一个是智商,一个情商,而且没有体积等限制条件,只有智商和情商的限制条件.但是我们可以将智商和(或者情商和)作为体积值,这个背包的最大体积不是数据的牛的智商和,而是牛的最大智商*牛数.
那么f[i][j]表示的含义是当选到第i头牛时且当前牛的智商和为j时情商的最大值. 对应01背包的 f[i][j] = max(f[i-1][j],f[i-1][j-zs[i]]+qs[i];
关于这里的j的理解我是理解成一个体积=牛的最大智商*牛数的背包,此时不管这头牛有没有算在里面都是这样大的容积.
那接下来有两个问题:
1.牛的智商和为负数
这里需要设置一个偏移量,比如base(代码里是sum).我们需要保证当牛的智商全为负数,他的下标也为正值,所以假设base = n*1000.由动态规划的递推过程可以发现.设置f[base] = 0, 那么接下来想取到>0的值的j需要是base+zs[i].我们可以发现,如果zs为负数,那么j的值一定在base左侧.否则右侧.
(看不懂base什么作用的一定没搞懂01背包,比如我)
关于分类dp网上基本讲了很多,就不讲了
2.怎么算答案
我们需要牛的智商为正,也就是只需要枚举base右边到最大范围.但是这样又会有疑问:可能会计算到不可能的牛的智商和.
这里又需要将f数组初始化为负无穷,这样才可以保证不可能出现的情况一直都是负值,只有j = base+zs[i]的才会被初始化.
注意两层for循环枚举的不同条件:我们需要保证数组不能越界,并且i不能从base开始.如果从base开始可能得不到上层递推的情况,
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 const int N = 110,M = 200010; 7 int zs[N],qs[N],f[M]; 8 int main() 9 { 10 freopen("in.txt","r",stdin); 11 int n; 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) scanf("%d%d",&zs[i],&qs[i]); 14 int sum = n*1000; 15 memset(f,-0x3f,sizeof f); f[sum] = 0;//这样初始化的意义是使不可能的牛的智商和的情况舍弃 16 for(int i=1;i<=n;i++) 17 { 18 if(zs[i]>0) 19 for(int j=sum*2;j>=zs[i];j--) 20 f[j] = max(f[j],f[j-zs[i]]+qs[i]); 21 if(zs[i]<=0) 22 for(int j=0;j-zs[i]<=sum*2;j++) 23 f[j] = max(f[j],f[j-zs[i]]+qs[i]); 24 } 25 int ans = 0; 26 for(int i=sum;i<=sum*2;i++) 27 if(f[i]>=0) ans = max(ans,f[i]+i-sum); 28 printf("%d\n",ans); 29 return 0; 30 }
总结:
- 写完这道题的最大感想就是要把01背包的递推过程再好好推一遍.板子都没理解做什么题= =
- 处理负数的常用操作:偏移