AcWing 1047. 糖果

原题链接

考察:01背包dp

错误思路:

       很容易想到f[i][j]代表最大值,i代表从前i个物品选,j代表此方案数量和的余数.

       像之前的01dp模板题一样,先给f[i][j]赋值f[i-1][j],再是当j>=sum[i]%k时再赋最大值.

错误原因已在注释标明

1     for(int i=1;i<=n;i++)
2         for(int j=0;j<k;j++)
3         {
4             f[i][j] = f[i-1][j];//如果加if判断,当f[i][0]的值只能由f[i-1][0]传递下来
5             if(j>=sum[i]%k) f[i][j] = max(f[i-1][j],f[i-1][(j-sum[i]%k+k)%k]+sum[i]);
6 //也就是说,f[i][0]的方案可以从减去sum[i]后的余数推来,如果加if判断就切断了这条路
7         }

如果+if判断,f[i][0]的值永远只能由余数为0的物品或者f[0][0]推来,但f[i][0]是可能由去掉第i个物品的sum[i]的和推来,加上sum[i]后余数正好为0

正确思路:

      去掉if判断,直接取最大值即可.但是注意,有些情况是不可能的,我们需要在初始状态将不可能的情况设置为最小

      这个加上sum[i]使得后面体积只要比sum[i]大就会max到更大值

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int N = 110;
 6 typedef long long ll;
 7 int sum[N],f[N][N];
 8 int main()
 9 {
10     int n,k;
11     scanf("%d%d",&n,&k);
12     memset(f,-0x3f,sizeof f);
13     f[0][0] = 0;
14     for(int i=1;i<=n;i++) scanf("%d",&sum[i]);
15     for(int i=1;i<=n;i++)
16         for(int j=0;j<k;j++)
17         {
18            f[i][j] = max(f[i-1][j],f[i-1][(j-sum[i]%k+k)%k]+sum[i]);
19         }
20     printf("%d\n",f[n][0]);
21     return 0;
22 }

 

总结:

  1. sum[i]一定要先%k,因为值有可能过大,我们需要保证j-sum[i]+k的值为正数
  2. 不能想当然套模板,一定要想想此情况能不能由其他情况推来

 2021.3.15 二刷AC,注意下<a[i]的余数的由来即可

posted @ 2021-02-01 15:10  acmloser  阅读(63)  评论(0编辑  收藏  举报