洛谷P1064金明的预算方案(好题难题,依赖的背包dp,或计算拓扑先后顺序+记忆化搜索)

题目链接: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 }

 

完。

 

posted @ 2018-11-04 17:15  RedBlack  阅读(191)  评论(0编辑  收藏  举报