今天来讲一道非常好的一道题,多种解法总有一款适合你
目录
题目:陶陶摘苹果
解法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;
}
希望阅者能够认真观看,争取拿下本题!顺便点个赞再走啊,写作不易。