zoukankan      html  css  js  c++  java
  • 洛谷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的基础上用数组保存下来即可!

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

  • 相关阅读:
    sql FLOAT字段使用like查询
    关于sql--时间范围查询重叠
    关于java时间类型比较
    前端-搜索无结果时,怎么把“暂无数据“显示出来?
    v-for动态赋值给不同的下拉框input
    Java的优先队列PriorityQueue详解
    软件体系架构阅读笔记八
    字符数组和字符串之间的转换
    Java快速输入输出
    软件体系架构阅读笔记七
  • 原文地址:https://www.cnblogs.com/redblackk/p/9880977.html
Copyright © 2011-2022 走看看