2013年腾讯马拉松程序设计大赛第四题——为湫湫制作食谱分析及源代码(C/C++实现)

         昨天试着做了一下腾讯马拉松程序设计大赛的题目,其中的第四题题目描述如下:


       对于吃货来说,过年最幸福的事就是吃了,没有之一!

  但是对于女生来说,卡路里(热量)是天敌啊!
  资深美女湫湫深谙“胖来如山倒,胖去如抽丝”的道理,所以她希望你能帮忙制定一个食谱,能使她吃得开心的同时,不会制造太多的天敌。

        当然,为了方便你制作食谱,湫湫给了你每日食物清单,上面描述了当天她想吃的每种食物能带给她的幸福程度,以及会增加的卡路里量。

        Input

  输入包含多组测试用例。
  每组数据以一个整数n开始,表示每天的食物清单有n种食物。
  接下来n行,每行两个整数a和b,其中a表示这种食物可以带给湫湫的幸福值(数值越大,越幸福),b表示湫湫吃这种食物会吸收的卡路里量。
  最后是一个整数m,表示湫湫一天吸收的卡路里不能超过m。

  [Technical Specification]
  1. 1 <= n <= 100
  2. 0 <= a,b <= 100000
  3. 1 <= m <= 100000

Output

  对每份清单,输出一个整数,即满足卡路里吸收量的同时,湫湫可获得的最大幸福值。

Sample Input

3

3 3

7 7

9 9

10

5

1 1

5 3

10 3

6 8

7 5

6

Sample Output

10

20

      解题思路:

      一开始,我没有意识到这是一个完全背包问题。我的思路是:先从每份清单中剔除那些热量超过了最大热量m的食物,然后计算每种食物的单位热量幸福值k(k=幸福值÷热量),并按k值降序排列,先选k值最大的食物,如果还有热量剩余,就选k值次大的食物,依此循环,直到剩余的热量值为0。

我的代码是这样的:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100;
//定义一个食物类
class val
{
public:
	int happy;//幸福值
	int heat;//热量
	float unitHapHea;//单位热量的幸福值
	val():happy(0),heat(0),unitHapHea(0.0){}//构造函数
};
//定义按降序排列的比较函数
bool greaterFun(const val& v1,const val& v2)
{
	if(v1.unitHapHea == v2.unitHapHea)
		return v1.heat < v2.heat;
	else
		return v1.unitHapHea > v2.unitHapHea;
}
int main()
{
	int n;
	int k = 0;
	int result[100];//保存结果的数组
	memset(result,0,sizeof(result));
	while (true)
	{
		cin>>n;
		if(n == 0)
			break;
		if(!(n >= 1 && n <= 100))
		{
			cout<<"输入的n无效,请重新输入!"<<endl;
			continue;
		}
		val happyAndHeat[100];
		int a,b;
		for(int i = 0;i < n;i++)
		{
			cin>>a>>b;
			if(!(a >= 0 && a <= 100000 && b >=0 && b <= 100000))
			{
				cout<<"输入的a或b值无效,请重新输入!"<<endl;
				cin>>a>>b;
			}
			happyAndHeat[i].happy = a;
			happyAndHeat[i].heat = b;
			happyAndHeat[i].unitHapHea = (double)a/(double)b;
		}
		int m = 0;
		cin>>m;
		vector<val> v;
		for(int i = 0;i < n;i++)
		{
			if(happyAndHeat[i].heat > m)//如果某种食物的热量值大于最大值m,则忽略
				continue;
			v.push_back(happyAndHeat[i]);
		}
		sort(v.begin(),v.end(),greaterFun);//按单位热量的幸福值降序排列
		int sum = 0;//幸福值总和
		int heatLeft = m;//剩余可用的热量值
		//按单位热量幸福值从大到小的顺序循环选择食物,直到剩余可用的热量值为零
		for (int i = 0;i < v.size();i++)
		{
			if(heatLeft == 0)
				break;
			if(heatLeft / v[i].heat == 0)
				continue;
			sum += heatLeft / v[i].heat * v[i].happy;
			heatLeft -= heatLeft / v[i].heat * v[i].heat;
		}
		result[k++] = sum;
	}
	//输出结果
	for(int i = 0;result[i] != 0;i++)
		cout<<result[i]<<endl;
}

但是在用

3

A食物  3 3

B食物  7 7

C 食物 9 9

10

       这个例子测试的时候出错了,输出结果是9而不是10,因为这三种食物的单位热量幸福值都是1,排列顺序是A B C,于是10/3=3,即选3份A食物,得到的幸福值是3×3=9。但是,这并不是最优的,因为这时的热量值是9,没达到最大值10,如果A和B各选一份,则得到的幸福值为10,是最右结果。

另一个例子

 3

A食物  3 3

B食物  8 8

C 食物 10 9

11

       因为C的单位热量幸福值最大,所以选一份C食物,得到幸福值10,但是A和B各选一份能得到最大的幸福值11。

对于第二个测试用例

5

1 1

5 3

10 3

6 8

7 5

6

是可以输出正确结果的,最大幸福值为20,即选2份第三种食物。

       分析后发现,这个方法有时候能得到正确结果,有时候得不到。原因在于我优先选择单位热量幸福值最大的食物,但是可能导致有剩余热量未被完全利用,即热量值没有达到最大值m,所以可能导致得不到最大幸福值。

       后来在网上看到别人的分析,再一想才发现这是一个完全背包问题,所以得出以下的正确解法:

#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
using namespace std;  

int dp[100006];  

int main()  
{  
	int t;  
	while(~scanf("%d",&t))  
	{  
		memset(dp,0,sizeof(dp));  
		int i,j,aim;  
		int ka[105],val[105];  
		for(i = 0; i<t; i++)  
			scanf("%d%d",&val[i],&ka[i]);  
		scanf("%d",&aim);  
		for(i = 0; i<t; i++) //状态转移方程  
		{  
			for(j = ka[i]; j<=aim; j++)  
			{  
				dp[j] = max(dp[j],dp[j-ka[i]]+val[i]);  
			}  
		}  
		printf("%d\n",dp[aim]);  
	}  
	scanf("%d",&t);
	return 0;  
}  

       经过编程竞赛题目的练习,才发觉自己要学的东西还有很多。能够正确建立模型对于快速、准确解答题目是至关重要的,所以对于类似于背包问题这样的经典模型,要深刻理解才能更好地应用。虽然一开始没有想出最优的解法,但是探索的过程还是极大地锻炼了思维能力。以后多学习才能不断进步!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值