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 } 

 

总结:

  1. 写完这道题的最大感想就是要把01背包的递推过程再好好推一遍.板子都没理解做什么题= =
  2. 处理负数的常用操作:偏移
posted @ 2021-02-01 22:57  acmloser  阅读(50)  评论(0编辑  收藏  举报