以“采药”为例谈剪枝
1746: 【NOIP05普及组】采药
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?
输入:
第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出:
一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
输入样例:
70 3
71 100
69 1
1 2
输出样例:
3
分析:M种药,每种药有各自的耗时和价值,要求在T时间内,采一些药使得总价值最大。
我们用01串表达采药的方案。以三种药为例:
111 表示 第1、2、3种药都采;
110表示 采第1、2种药第三种药不采;
……
3种药总共有8种方案:
000
001
010
011
100
101
110
111
我们可以在总耗时小于T的那些方案中,寻找价值和最大的方案。
首先我们来写一个代码产生表示方案的01串。
1 # include <bits/stdc++.h> 2 using namespace std; 3 int n,a[101]; 4 void dfs(int dep){ 5 if(dep>n){ 6 for (int i=1;i<=n;i++) cout<<a[i]; 7 cout<<endl; 8 } 9 else{ 10 a[dep]=1; 11 dfs(dep+1); 12 a[dep]=0; 13 dfs(dep+1); 14 } 15 } 16 17 int main(){ 18 cin>>n; 19 dfs(1); 20 return 0 ; 21 22 return 0; 23 }
改进一下代码:
1 # include <bits/stdc++.h> 2 using namespace std; 3 int n,a[101]; 4 void dfs(int dep){ 5 if(dep>n){ 6 for (int i=1;i<=n;i++) cout<<a[i]; 7 cout<<endl; 8 } 9 else{ 10 for(int i=0;i<=1;i++){ 11 a[dep]=1-i; 12 dfs(dep+1); 13 } 14 } 15 } 16 17 int main(){ 18 cin>>n; 19 dfs(1); 20 return 0 ; 21 22 }
方案形成后,打擂台记录最大价值
1 // http://www.jzoj.cn/problem.php?id=1746 2 // 20% 3 4 # include <bits/stdc++.h> 5 using namespace std; 6 int n,a[101]; 7 int W,w[101],v[101],maxv; 8 9 void dfs(int dep){ 10 if(dep>n) //方案处理,计算这个方案的总重量和总价值 11 { 12 int tw=0,tv=0; 13 for (int i=1;i<=n;i++) { 14 tw+=a[i]*w[i]; 15 tv+=a[i]*v[i]; 16 } 17 if(tw<=W && tv>maxv) { 18 maxv=tv; 19 } 20 } 21 else{ 22 for(int i=0;i<=1;i++){ 23 a[dep]=1-i; 24 dfs(dep+1); 25 } 26 27 } 28 } 29 30 int main(){ 31 cin>>W>>n; 32 for(int i=1; i<=n; i++) cin>>w[i]>>v[i]; 33 dfs(1); 34 cout<<maxv<<endl; 35 return 0 ; 36 37 }
改进代码,我们在方案产生时,记录总耗时和总价值:
1 // http://www.jzoj.cn/problem.php?id=1746 2 // 25% 3 4 # include <bits/stdc++.h> 5 using namespace std; 6 int n,a[101]; 7 int W,w[101],v[101],maxv; 8 int tw,tv; 9 10 void dfs(int dep){ 11 if(dep>n) //方案处理,计算这个方案的总重量和总价值 12 { 13 if(tv>maxv) maxv=tv; 14 } 15 else{ 16 for(int i=0;i<=1;i++){ 17 a[dep]=1-i; 18 if(tw+a[dep]*w[dep]<=W){ 19 tw+=a[dep]*w[dep]; 20 tv+=a[dep]*v[dep]; 21 dfs(dep+1); 22 tw-=a[dep]*w[dep]; 23 tv-=a[dep]*v[dep]; 24 } 25 } 26 27 } 28 } 29 30 int main(){ 31 cin>>W>>n; 32 for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; 33 dfs(1); 34 cout<<maxv<<endl; 35 return 0 ; 36 37 }
有些方案还没形成时我们就知道不可能有更优解了,来一次剪枝:
1 // http://www.jzoj.cn/problem.php?id=1746 2 // 30% 3 # include <bits/stdc++.h> 4 using namespace std; 5 int n,a[101]; 6 int W,w[101],v[101],maxv; 7 int tw,tv; 8 9 int rest(int dep) //记录剩余药草的总价值 10 { 11 int s=0; 12 for(int i=dep;i<=n;i++) s+=v[i]; 13 return s; 14 } 15 16 void dfs(int dep){ 17 if(dep>n) //方案处理,计算这个方案的总重量和总价值 18 { 19 if(tv>maxv) maxv=tv; 20 } 21 else{ 22 for(int i=0;i<=1;i++){ 23 a[dep]=1-i; 24 if(tw+a[dep]*w[dep]<=W && tv+a[dep]*v[dep]+rest(dep+1)>maxv ){ 25 tw+=a[dep]*w[dep]; 26 tv+=a[dep]*v[dep]; 27 dfs(dep+1); 28 tw-=a[dep]*w[dep]; 29 tv-=a[dep]*v[dep]; 30 } 31 } 32 33 } 34 } 35 36 int main(){ 37 cin>>W>>n; 38 for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; 39 40 dfs(1); 41 cout<<maxv<<endl; 42 return 0 ; 43 44 45 }
最后还是解决不了,M过大的问题,终结方法DP:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int t,m,w[105],v[105],f[105][1005]; 4 int main() 5 { 6 cin>>t>>m; 7 for(int i=1; i<=m; i++) 8 cin>>w[i]>>v[i]; 9 for(int i=1; i<=m; i++) 10 { 11 for(int j=t; j>=0; j--) 12 { 13 if(j>=w[i]) 14 f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i-1][j]); 15 else f[i][j]=f[i-1][j]; 16 } 17 } 18 cout<<f[m][t]; 19 }

浙公网安备 33010602011771号