题目链接:https://www.luogu.org/problemnew/show/P1060
本题可太多做法,可dp,可暴力递归,可记忆化,这里不讲dp,只是搜索递归专场。
需要注意的是,每个物品只能取一次!
暴力正向递归的话有两个看待(思考)角度,角度不同递归方式和复杂度也不一样。
一:
1.按照题意纯正向暴力递归下去(无方向性,就找到所有情况),找到所有<=n的匹配方案去最大的一个。(时间超时)
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstring> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=1e6+5; 9 char f[1005][1005]; 10 int v[maxn],w[maxn]; 11 int vis[maxn]; 12 int n,m; 13 int ans; 14 15 void so(int sums,int sumv) 16 { 17 //cout<<sums<<endl; 18 ans=max(ans,sums); 19 if(sumv>=n) return; 20 21 for(int i=1;i<=m;i++) 22 { 23 if(sumv+v[i]<=n && vis[i]==0) 24 { 25 vis[i]=1; 26 so(sums+v[i]*w[i],sumv+v[i]); 27 vis[i]=0; 28 } 29 } 30 } 31 32 int main() 33 { 34 cin>>n>>m; 35 for(int i=1;i<=m;i++) cin>>v[i]>>w[i]; 36 37 so(0,0); 38 39 cout<<ans<<endl; 40 41 return 0; 42 43 }
2.排序剪枝!这其实是在1上面的剪枝优化,因为1实在没有任何优化,纯暴力找到所有情况。但可以发现经过排序后,若加上一个单价>1000,后面必定>1000,这就是很好的剪枝!没必要进行下去了。
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstring> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=1e6+5; 9 int n,m; 10 int ans; 11 struct px 12 { 13 int v; 14 int w; 15 int ji; 16 }T[maxn]; 17 bool cmp(px aa,px bb) 18 { 19 return aa.v<bb.v; 20 } 21 22 void so(int last,int sums,int sumv) 23 { 24 //cout<<sums<<endl; 25 ans=max(ans,sums); 26 //if(last==m) return; 27 28 for(int i=last+1;i<=m;i++) 29 { 30 if(sumv+T[i].v>n) break; 31 32 so(i,sums+T[i].ji,sumv+T[i].v); 33 34 } 35 } 36 37 int main() 38 { 39 cin>>n>>m; 40 for(int i=1;i<=m;i++) 41 { 42 cin>>T[i].v>>T[i].w; 43 T[i].ji=T[i].v*T[i].w; 44 } 45 46 sort(T+1,T+1+m,cmp); 47 //for(int i=1;i<=m;i++) cout<<T[i].v<<' '<<T[i].w<<endl; 48 so(0,0,0); 49 50 cout<<ans<<endl; 51 52 return 0; 53 54 }
二:
3.看透题目本质,理解dfs本质(有方向性暴力递归下去)!本质就是每个物品放与不放的问题,这就是递归的两个方向!找到所有<=1000的每个物品放与不放的任意组合。这就类似dfs上下左右,上下方向的本质,有方向地递归下去找到所有合适的情况。(200ms)
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstring> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=1e6+5; 9 int n,m; 10 int ans; 11 struct px 12 { 13 int v; 14 int w; 15 int ji; 16 }T[maxn]; 17 bool cmp(px aa,px bb) 18 { 19 return aa.v<bb.v; 20 } 21 22 void so(int last,int sums,int sumv) 23 { 24 //cout<<sums<<endl; 25 ans=max(ans,sums); 26 if(last==m+1) return; 27 28 if(sumv+T[last].v<=n) so(last+1,sums+T[last].ji,sumv+T[last].v); 29 if(sumv<=n) so(last+1,sums,sumv); 30 } 31 32 int main() 33 { 34 cin>>n>>m; 35 for(int i=1;i<=m;i++) 36 { 37 cin>>T[i].v>>T[i].w; 38 T[i].ji=T[i].v*T[i].w; 39 } 40 41 //sort(T+1,T+1+m,cmp); 42 //for(int i=1;i<=m;i++) cout<<T[i].v<<' '<<T[i].w<<endl; 43 so(1,0,0); 44 45 cout<<ans<<endl; 46 47 return 0; 48 49 }
4.既然你会了,想到了3.的方向性选择递归,那么这题的记忆化递归就好写了,在3的基础上用数组保存下来即可!
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstring> 6 #include <cmath> 7 using namespace std; 8 const int maxn=1e5+5; 9 int v[maxn],w[maxn],ji[maxn]; 10 int f[30][100005]; 11 int n,m; 12 13 int so(int step,int sumv) 14 { 15 if(f[step][sumv]) return f[step][sumv]; 16 if(step==m+1) return 0; 17 18 int ans=0,ls=0,rs=0; 19 if(sumv+v[step]<=n) { ls=ls+ji[step]+so(step+1,sumv+v[step]); } 20 if(sumv<=n) rs=rs+so(step+1,sumv); 21 22 ans=max(ls,rs); 23 return f[step][sumv]=ans; 24 } 25 26 int main() 27 { 28 cin>>n>>m; 29 for(int i=1;i<=m;i++) 30 { 31 cin>>v[i]; 32 cin>>w[i]; 33 ji[i]=v[i]*w[i]; 34 } 35 36 int ans=so(1,0); 37 38 cout<<ans<<endl; 39 40 return 0; 41 }
5.最后,如果你都熟悉的话,甚至可以排序剪枝+记忆化搜索一起使用,相信那将会是很强的