一、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 )。现在终于写完了——刚好下课!
浙公网安备 33010602011771号