题目链接:https://www.luogu.org/problemnew/show/P1064
这里不讲dp,只讲搜索记忆化。这题看到时比较难,无从入手,但想通后就和上一题开心的金明一样了,只不过搜索时由2选择方向变成了5个选择方向而已,其他基本不变。
.刚开始想直接在上一题记忆化的代码上修改一下就交了,但一直WA,多了附件,状态没法存啊。。。想了好长时间,直到发现大佬的这段话:
这个题的决策是五个,分别是:
1.不选,然后去考虑下一个
2.选且只选这个主件
3.选这个主件,并且选附件1
4.选这个主件,并且选附件2
5.选这个主件,并且选附件1和附件2.
这个。。。很好想吧。。。
顿时恍然大悟,没法直接存状态,就先拓扑计算出先后顺序啊,然后就只是增加方向,和上一题一样了!(另外,在排序时有个下标混乱的bug坑点要注意,代码有注释)
先上一份原来的基础记忆化搜索,本题的搜索就是在此基础上改的
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 }
本题对上基础搜索修改:
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <vector> 5 #include <cstdio> 6 #include <cstring> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=1e6+5; 10 int f[65][32005]; 11 int n,m; 12 struct px 13 { 14 int v; 15 int ji; 16 int w; 17 int q; 18 int ii;//记录原来下标,排序后要用到很关键! 19 }T[maxn]; 20 bool cmp(px aa,px bb) 21 { 22 return aa.q<bb.q; 23 } 24 vector<px> vec[maxn]; 25 26 27 int so(int step,int sumv) 28 { 29 //只递归主件,附件直接跳过 30 if(f[step][sumv]!=-1) return f[step][sumv]; 31 if(step==m+1) return 0; 32 33 int ans=0,l1=0,l2=0,l3=0,l4=0,l5=0; 34 35 if(sumv<=n) l1+=+so(step+1,sumv); 36 if(sumv+T[step].v<=n) { l2+=T[step].ji+so(step+1,sumv+T[step].v); } 37 int I=T[step].ii; 38 int len=vec[I].size(); 39 if(len>=1 && sumv+vec[I][0].v<=n) { l3+=vec[I][0].ji+so(step+1,sumv+vec[I][0].v); } 40 if(len>=2 && sumv+vec[I][1].v<=n) { l4+=vec[I][1].ji+so(step+1,sumv+vec[I][1].v); } 41 if(len>=3 && sumv+vec[I][2].v<=n) { l5+=vec[I][2].ji+so(step+1,sumv+vec[I][2].v); } 42 43 ans=max(ans,l1); 44 ans=max(ans,l2); 45 ans=max(ans,l3); 46 ans=max(ans,l4); 47 ans=max(ans,l5); 48 return f[step][sumv]=ans; 49 } 50 51 int main() 52 { 53 ios::sync_with_stdio(false); cin.tie(0); 54 55 cin>>n>>m; 56 for(int i=1;i<=m;i++)//1.输入 57 { 58 cin>>T[i].v>>T[i].w>>T[i].q; 59 60 T[i].ji=T[i].v*T[i].w; 61 T[i].ii=i; 62 if(T[i].q>0)//可以的话存入附件1,2 63 { 64 int x=T[i].q; 65 66 if(T[x].v+T[i].v<=n) 67 { 68 px t; 69 t.v=T[x].v+T[i].v; 70 t.ji=T[x].ji+T[i].ji; 71 vec[x].push_back(t); 72 } 73 } 74 } 75 76 for(int i=0;i<=m+1;i++)//2.记忆数组初始化 77 { 78 for(int j=0;j<=n+1;j++) 79 { 80 f[i][j]=-1; 81 } 82 } 83 84 sort(T+1,T+1+m,cmp);//3.排序,去掉附件,只留主件(这一点很容错是隐藏的bug错误不然前面必错,找了好长时间的bug,QAQ~~~) 85 for(int i=1;i<=m;i++)//等等,好像还不能排序...一排原来存好的vector下标就乱了对不上了!! 86 { //所以,关键是这,结构体中用一个变量ii存下原来的属于自己的下标,这样排序后还能记录原来下标 87 if(T[i].q>0) 88 { 89 m=i-1; 90 break; 91 } 92 } 93 94 for(int i=1;i<=m;i++)//4.试试能不能2附件同时取,存入这种情况 95 { 96 int I=T[i].ii; 97 int len=vec[I].size(); 98 if(len>=2) 99 { 100 int v1=vec[I][0].v-T[i].v; 101 int ji1=vec[I][0].ji-T[i].ji; 102 int v2=vec[I][1].v-T[i].v; 103 int ji2=vec[I][1].ji-T[i].ji; 104 105 if(T[i].v+v1+v2<=n) 106 { 107 px t; 108 t.v=T[i].v+v1+v2; 109 t.ji=T[i].ji+ji1+ji2; 110 vec[I].push_back(t); 111 } 112 } 113 } 114 115 /*cout<<endl;//输出准备工作做完的序列,便于查找错误,你只要排好了按照上一题的记忆化搜索就一定没错! 116 for(int i=1;i<=m;i++) 117 { 118 cout<<T[i].v<<' '<<T[i].ji<<' '<<T[i].ii<<endl; 119 int I=T[i].ii; 120 for(int j=0;j<vec[I].size();j++) 121 { 122 cout<<vec[I][j].v<<' '<<vec[I][j].ji<<endl; 123 } 124 }*/ 125 126 int ans=so(1,0); 127 cout<<ans<<endl; 128 129 return 0; 130 }
完。