【算法每日一练]-背包类型 篇3 陶陶摘苹果

今天来讲一道非常好的一道题,多种解法总有一款适合你

目录

题目:陶陶摘苹果

  解法1:背包思想

 二维写法(60ms)

 一维写法:(快了20ms)

  解法2:记忆化写法(130ms)

  解法3:贪心(18ms)


  

题目:陶陶摘苹果

    


     

 解法1:背包思想

每个苹果都有两种状态,要么拿,要么不拿,那就是01背包,体力对应容量,个数对应价值即可

     

 二维写法(60ms)

 dp[i][j] : i表示已遍历的苹果,j表示已耗体力,dp表示取到的苹果,最终求dp[n][s]即可
 取不了:dp[i][j]=dp[i-1][j]  
 能取  : dp[i][j]=max(dp[i-1][j-a[i]]+1,dp[i][j])
 你用的是二维背包,这样多了一维来存i,所以不需要倒序遍历背包!!!

#include<iostream>                      //陶陶找苹果(动归)(背包模型)      (60毫秒)
using namespace std;
int dp[5005][1001];               
int xi[5005],yi[5005],n,s,a,b;
int main(){
    cin>>n>>s>>a>>b;
    for(int i=1;i<=n;i++){
        cin>>xi[i]>>yi[i];
    }
    for(int i=1;i<=n;i++)    //枚举考虑每一个苹果
    for(int j=0;j<=s;j++){   //枚举背包大小
        dp[i][j]=dp[i-1][j]; //取不了(if不执行)就直接和之前的苹果数一样
        if(xi[i]<=a+b&&j>=yi[i])//如果能够取(if执行)
        dp[i][j]=dp[i-1][j-yi[i]]+1>dp[i][j]?dp[i-1][j-yi[i]]+1:dp[i][j];//动态转移方程。max函数运行太慢,我们这里选择三目运算符取较大值
    }
    cout<<dp[n][s];//因为是从前向后递推,因此接收最终答案的位置也从最前面转到了最后面
    return 0;
}

  

一维写法:(快了20ms)

 取不了:dp[i][j]=dp[i-1][j]  
 能取  : dp[i][j]=max(dp[i-1][j-a[i]]+1,dp[i][j])

你会发现仅用到了i-1的数据,那么可以降一维:f[j] =max(f[j-a[i]]+1,f[j])

写成 if(xi[i]<=a+b)f[j]=max(f[j-yi[i]]+1,f[j])即可

#include<iostream>                      //陶陶找苹果(动归)(背包模型)      (40毫秒)
using namespace std;             
int xi[5005],yi[5005],n,s,a,b;
int f[5005];
int main(){
    cin>>n>>s>>a>>b;
    for(int i=1;i<=n;i++){
        cin>>xi[i]>>yi[i];
    }
    for(int i=1;i<=n;i++)    //枚举考虑每一个苹果
    for(int j=s;j>=yi[i];j--){   //枚举背包大小
    	if(xi[i]<=a+b)f[j]=max(f[j-yi[i]]+1,f[j]);
        
    }
    cout<<f[s];//因为是从前向后递推,因此接收最终答案的位置也从最前面转到了最后面
    return 0;
}

    


    

 解法2:记忆化写法(130ms)

确定状态:dfs(num,rest)对应mem[num][rest],表示(从后向前遍历)遍历到第num苹果,且消耗体力rest对应的最优解,故dfs(1,s)即可。

映射关系:dfs(num,rest)=max( dfs(num+1,rest),dfs(num+1,rest-yi[num])+1 )

    

 说到记忆化,你可以以本题为例,尝试输出每次递归的num和rest , 即输出进入一次dfs就输出一次对应的(num,rest),  你会发现有许多重复的,或者说是,特别特别多重复!

但是实际上最多只有五百万种情况,因为5000*5000嘛。而搜索就要走许多重复状态。其实你是能看到的,递归的话需要递归2^5000次,因为一共5000个苹果嘛,每个苹果两个状态。这就是记忆化的好处

#include<iostream> // 陶陶摘苹果P1478  (记忆化搜索)(130毫秒)
#include<cstring>               
using namespace std;
int n,s,a,b,ans;
int xi[5005],yi[5005];
int mem[5005][1001];
int dfs(int num,int rest){
    if(num>n) return 0; //结束递归          
    if(mem[num][rest]!=-1) {return mem[num][rest]; }//记忆化
    int maxn=dfs(num+1,rest);//不取
    if(xi[num]<=a+b&&rest>=yi[num]){
        int t=dfs(num+1,rest-yi[num])+1;//取
        maxn=t>maxn?t:maxn;
    }
    return mem[num][rest]=maxn;//返回并保存
}
int main(){
    cin>>n>>s>>a>>b;//输入苹果数,力气,椅子高度,陶陶能够到高度
    memset(mem,-1,sizeof(mem));
    for(int i=1;i<=n;i++){
        cin>>xi[i]>>yi[i];
    }
    cout<<dfs(1,s);
    return 0;
} 

    


    

解法3:贪心(18ms)

直接在输入的时候就剔除掉根本拿不到的苹果,再排序,之后再贪心的去拿苹果,因为每个苹果价值相同,所以我们可以贪心来做。而且速度极快

#include <iostream>                     //陶陶摘苹果(贪心)      (18毫秒)
#include <algorithm> 
using namespace std;
int n,s,a,b,inde,ans;          //不要写成"index",要写成"inde"
struct apple{
    int xi,yi;
}ap[50005];
int cmp(apple x_,apple y_){
    return x_.yi<y_.yi;
}
int main(){
    cin>>n>>s>>a>>b;int x,y;
    for(int i=1;i<=n;i++){
        cin>>x>>y;
        if(x<=a+b){             //从根源上解决问题
            inde++; 
			ap[inde].xi=x; ap[inde].yi=y;
        }
    }
    sort(ap+1,ap+inde+1,cmp);
    for(int i=1;s>=ap[i].yi&&i<=inde;i++){
        ans++;
        s-=ap[i].yi;
    }
    cout<<ans;
    return 0;
}

希望阅者能够认真观看,争取拿下本题!顺便点个赞再走啊,写作不易。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值