洛谷p1060开心的金明(dp,正向暴力递归+剪枝或记忆化)

题目链接: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的基础上用数组保存下来即可!

5.最后,如果你都熟悉的话,甚至可以排序剪枝+记忆化搜索一起使用,相信那将会是很强的

猜你喜欢

转载自www.cnblogs.com/redblackk/p/9880977.html