约翰有一架用来称牛的体重的天平。与之配套的是N(1≤N≤1000)个已知质量的砝码(所有砝码质量的数值都在32位带符号整数范围内)。每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1≤C≤230)时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量,你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。
输入格式:第1行输入两个用空格隔开的正整数N和C。
第2到N+1行:每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。
输出格式: 输出一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。
样例输入: 3 15
1
10
20
样例输出: 11
涉及知识点:dfs深度优先搜索 dp动态规划
思路一:运用dfs搜索
(1)#include <stdio.h>
int a[1005],s[1005];//数组a用来存放每个砝码的质量,数组s用来存放前缀和
int n,c,ans,cur,i;
//n为砝码个数,c为最大承重,ans存储当前找到的最大砝码重量,cur当前已选择的砝码总质量
void dfs(int cur,int n)//定义一个dfs函数,形参为cur和n;
{
if(cur+s[n]<=ans) return;
//剪枝操作(减少搜索路径):cur+s[n]表示当前状态下,如果继续选择剩余的所有物品,背包总重量可能达到的最大值,如果最大值都小于ans,则没有搜索的必要;
ans=ans>cur?ans:cur;//更新ans为当前砝码重量的最大值
if(n==0) return;//结束递归的出口
if(cur+a[n]<=c)
dfs(cur+a[n],n-1);
//如果当前砝码质量加上a[n]还小于最大承重,则cur的值变成cur加上a[n],并再次调用dfs函数
else
dfs(cur,n-1);//如果当前砝码质量加上a[n]还大于最大承重,则cur的值不变,又因为此时n不等于0,则也要再次递归
}
int main(void)
{
scanf("%d%d",&n,&c);
for(i=1;i<=n;i++)
{ scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];}
dfs(0,n);//初始时cur=0
printf("%d",ans);//虽然dfs函数是void类型,但是ans是全局变量,在被调函数中也可以修改
return 0;
}
(2)没有注释的代码:
#include <stdio.h>
int a[1005],s[1005];
int n,c,ans,cur,i;
void dfs(int cur,int n)
{
if(cur+s[n]<=ans) return;
ans=ans>cur?ans:cur;
if(n==0) return;
if(cur+a[n]<=c)
dfs(cur+a[n],n-1);
else
dfs(cur,n-1);
}
int main(void)
{
scanf("%d%d",&n,&c);
for(i=1;i<=n;i++)
{ scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];}
dfs(0,n);
printf("%d",ans);
return 0;
}
思路二:dp动态规划
#include <stdio.h>
#define Max_c 230//c的值最大是230
int dp[Max_c+1];
//dp数组中的dp[i]表示当前承重为j时,所能称得的最大重量。dp[0]初始化为0,因为承重为0时无法撑出任何重量;dp[i]=0,因为在没放砝码时,称出的重量是0;
int main(void)
{
int n,c;
scanf("%d%d",&n,&c);
int weights[n];
for(int i=0;i<n;i++)
scanf("%d",&weights[i]);
for(int i=0;i<n;i++)//遍历每一个砝码
for(int j=c;j>=weights[i];j--)//遍历从背包最大重量c到当前砝码重量weights[i]之间的重量,用于判断是否将当前物品放入背包。j>=weights[i]确保在容量大于等于当前物品时才有可能放入该物品
{ dp[j]=dp[j]>dp[j-weights[i]]+weights[i]?dp[j]:dp[j-weights[i]]+weights[i]; }
//dp[j]表示不加入该物品时的背包最大重量,dp[j-weights[i]]表示放上该物品之后剩余承重(j-weights[i])能达到的最大质量(保证每一步都是最优解),比较dp[j]和dp[j-weights[i]]+weights[i]取最大值。
printf("%d",dp[c]);
return 0;
}
//倒叙遍历是为了避免当前砝码被重复计算,从而满足动态规划的递推原则。
不加注释的代码:
#include <stdio.h>
#define Max_c 230
int dp[Max_c+1];
int main(void)
{
int n,c;
scanf("%d%d",&n,&c);
int weights[n];
for(int i=0;i<n;i++)
scanf("%d",&weights[i]);
for(int i=0;i<n;i++)
for(int j=c;j>=weights[i];j--)
{ dp[j]=dp[j]>dp[j-weights[i]]+weights[i]?dp[j]:dp[j-weights[i]]+weights[i]; }
printf("%d",dp[c]);
return 0;
}