一、01背包:P1048 [NOIP2005 普及组] 采药

  f[i] 表示在 i 时间内所能采到的最大药草,容易得到 f[i]=max(f[i],f[i-w[i]]+v[i]) 。

  然而存在重复采同一株药的情况,于是设计新的状态 f[i][j] 表示在前 i 株药草中,时间为 j 所能采到的最多的药草。

  由此推出新的转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]) 。意思也就很好理解( 然而我花了很久才理解 qwq )。

  状态由前 i-1 株药草时间为 j-w[i] 的状态推过来,再取一个 max 就好了。(前提是 j 大于 w[i] )下面放代码:

 1 #include<iostream>
 2 #define N 101
 3 using namespace std;
 4 int t,n,w[N],v[N],f[N][N*10];
 5 int main()
 6 {
 7     cin>>t>>n;
 8     for(int i=1;i<=n;i++)
 9         cin>>w[i]>>v[i];
10     for(int i=1;i<=n;i++)
11         for(int j=1;j<=t;j++)
12         {
13             f[i][j]=f[i-1][j];
14             if(j<w[i])    continue; 
15             f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i][j]);
16         }
17     cout<<f[n][t]<<'\n';
18     return 0;
19 }

  这是二位数组写法。但我们发现求 f[i][j] 时只会用到 f[i-1][j] 的数据,所以可以优化一维。代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 int t,n,w[101],v[101],f[1010];
 4 int main()
 5 {
 6     cin>>t>>n;
 7     for(int i=1;i<=n;i++)
 8         cin>>w[i]>>v[i];
 9     for(int i=1;i<=n;i++)
10         for(int j=t;j>=w[i];j--)
11             f[j]=max(f[j-w[i]]+v[i],f[j]);
12     cout<<f[t]<<'\n';
13     return 0;
14 }

二、完全背包

  上面代码真的短,但是要注意, j 是倒序枚举,防止一株药被采多次。那完全背包正序枚举不就行了吗?没错,就是这样!代码如下:(P1616 疯狂的采药)

 1 #include<iostream>
 2 using namespace std;
 3 int w[10010],v[10010];
 4 long long t,n,f[10000010];
 5 int main()
 6 {
 7     cin>>t>>n;
 8     for(int i=1;i<=n;i++)
 9         cin>>w[i]>>v[i];
10     for(int i=1;i<=n;i++)
11         for(int j=w[i];j<=t;j++)
12             f[j]=max(f[j-w[i]]+v[i],f[j]);
13     cout<<f[t]<<'\n';
14     return 0;
15 }

三、写题心得:

  背包直到前几天才真正理解了一点,又因为树链剖分耽搁了一下,所以今天才来写这个博客( 还是用的信息课 qwq )。现在终于写完了——刚好下课!

posted on 2023-03-14 15:22  trh0630  阅读(887)  评论(0)    收藏  举报