zoukankan      html  css  js  c++  java
  • DFS——>记忆化搜索——>递推

    以洛谷P1802  5倍经验日 为例

    https://www.luogu.org/problem/show?pid=1802

    题目背景

    现在乐斗有活动了!每打一个人可以获得5倍经验!absi2011却无奈的看着那一些比他等级高的好友,想着能否把他们干掉。干掉能拿不少经验的。

    题目描述

    现在absi2011拿出了x个迷你装药物(嗑药打人可耻….),准备开始与那些人打了

    由于迷你装一个只能管一次,所以absi2011要谨慎的使用这些药,悲剧的是,没到达最少打败该人所用的属性药了他打人必输>.<所以他用2个药去打别人,别人却表明3个药才能打过,那么相当于你输了并且这两个属性药浪费了。

    现在有n个好友,有输掉拿的经验、赢了拿的经验、要嗑几个药才能打过。求出最大经验(注意,最后要乘以5)

    输入输出格式

    输入格式:

    第一行两个数,n和x

    后面n行每行三个数,分别表示输了拿到的经验(lose[i])、赢了拿到的经验(win[i])、打过要至少使用的药数量(use[i])。

    输出格式:

    一个整数,最多获得的经验

    输入输出样例

    输入样例#1:
    6 8
    21 52 1
    21 70 5
    21 48 2
    14 38 3
    14 36 1
    14 36 2
    
    输出样例#1:
    1060

    说明

    【Hint】

    五倍经验活动的时候,absi2011总是吃体力药水而不是这种属性药>.<

    【数据范围】

    对于10%的数据,保证x=0

    对于30%的数据,保证n<=10,x<=20

    对于60%的数据,保证n<=100,x<=100, 10<=lose[i], win[i]<=100,use[i]<=5

    对于100%的数据,保证n<=1000,x<=1000,0<lose[i]<=win[i]<=1000000,0<=use[i]<=1000

    明显的一个01背包

    先说本题要注意的地方

    1、答案要用long long

    2、如果是体力为0也可以打,+lose[i]

    裸的搜索版本 ,TLE

    now表示当前搜索哪个对手,expe表示当前经验值,potion表示剩下的药水瓶数

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,x;
    int lose[1001],win[1001],need[1001];
    long long ans;
    void dfs(int now,long long expe,int potion)
    {
        ans=max(ans,expe);
        if(now>n) return;
        if(need[now]<=potion)  dfs(now-1,expe+win[now],potion-need[now]);
        dfs(now-1,expe+lose[now],potion); 
    }
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&lose[i],&win[i],&need[i]);
        dfs(n,0,x);
        printf("%lld",ans*5);
    }

    有人可能要问为什么搜索dfs过程的now是从n到1,能不能从1到n?

    答案是完全可以,因为搜索的结果与搜索顺序无关

    下面的记忆化搜索now也可以改成从1到n

    记忆化搜索版本  55ms /  23.51MB

    可以发现主要的区别就是dfs有了返回值

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,x;
    int lose[1001],win[1001],need[1001];
    long long f[1002][1001];
    long long dfs(int now,int potion)
    {
        if(!now) return 0;//边界
        if(f[now][potion]) return f[now][potion];//关键在这一句,如果这一状态在之前已经计算过了,直接return
        if(need[now]<=potion)  f[now][potion]=max(f[now][potion],dfs(now-1,potion-need[now])+win[now]);     
        f[now][potion]=max(f[now][potion],dfs(now-1,potion)+lose[now]);
        return f[now][potion];
    }
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&lose[i],&win[i],&need[i]);
        printf("%lld",dfs(1,x)*5);
    }

    递推版本  30ms /  23.5MB

    由此可以看到递推与记忆化搜索的不同之处:

    记忆化搜索,本质还是搜索,所以它的更新是从下往上的,即由后一个状态来更新

    当转化成递推的形式时,更新是从上往下的,即由前一个状态来更新

    所以转化成递推时,要将记忆化搜索的更新过程反过来

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,x;
    int lose[1001],win[1001],need[1001];
    long long f[1001][1001];
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&lose[i],&win[i],&need[i]);
        for(int i=1;i<=n;i++)
         for(int j=x;j>=0;j--)
          if(j>=need[i]) f[i][j]=max(f[i-1][j-need[i]]+win[i],f[i-1][j]+lose[i]);
          else f[i][j]=f[i-1][j]+lose[i];
        printf("%lld",f[n][x]*5);
    }

    递推版本的优化,压去第一维  19ms /  16.04MB

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,x;
    int lose[1001],win[1001],need[1001];
    long long f[1001];
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&lose[i],&win[i],&need[i]);
        for(int i=1;i<=n;i++)
         for(int j=x;j>=0;j--)
          if(j>=need[i]) f[j]=max(f[j-need[i]]+win[i],f[j]+lose[i]);
          else f[j]+=lose[i];
        printf("%lld",f[x]*5);
    }
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,x;
    int lose[1001],win[1001],need[1001];
    long long f[1002][1001];
    long long dfs(int now,int potion)
    {
        if(now>n) return 0;
        if(need[now]<=potion) 
        {
            if(f[now+1][potion-need[now]]) f[now][potion]=max(f[now][potion],f[now+1][potion-need[now]]);
            else f[now][potion]=max(f[now][potion],dfs(now+1,potion-need[now])+win[now]);     
        }
        if(f[now+1][potion])  f[now][potion]=max(f[now][potion],f[now+1][potion]);
        else f[now][potion]=max(f[now][potion],dfs(now+1,potion)+lose[now]);
        return f[now][potion];
    }
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&lose[i],&win[i],&need[i]);
        printf("%lld",dfs(0,x)*5);
    }
    错误的记忆化搜索

    求助路过大佬

  • 相关阅读:
    简单的C语言编译器--语法分析器
    简单的C语言编译器--词法分析器
    简单的C语言编译器--概述
    最短路径算法
    拓扑排序和关键路径
    图的连通性
    最小生成树
    图的搜索
    gcc O2优化选项对内嵌汇编以及函数递归调用的影响
    gcc 在c代码中内嵌汇编调用c函数: 只是证明曾经我来过
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6367147.html
Copyright © 2011-2022 走看看