信息学奥赛一本通(1225:金银岛)

1225:金银岛


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 8123     通过数: 4334

【题目描述】

某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属。但是他只带着一个口袋,口袋至多只能装重量为w的物品。岛上金属有s个种类, 每种金属重量不同,分别为n1,n2,...,ns,同时每个种类的金属总的价值也不同,分别为v1,v2,...,vs。KID想一次带走价值尽可能多的金属,问他最多能带走价值多少的金属。注意到金属是可以被任意分割的,并且金属的价值和其重量成正比。

【输入】

第1行是测试数据的组数k,后面跟着k组输入。

每组测试数据占3行,第1行是一个正整数w(1≤w≤10000),表示口袋承重上限。第2行是一个正整数s(1≤s≤100),表示金属种类。第3行有2s个正整数,分别为n1,v1,n2,v2,...,ns,vs分别为第一种,第二种,...,第s种金属的总重量和总价值(1≤ni≤10000,1≤vi≤10000)。

【输出】

k行,每行输出对应一个输入。输出应精确到小数点后2位。

【输入样例】

2
50
4
10 100 50 30 7 34 87 100
10000
5
1 43 43 323 35 45 43 54 87 43

【输出样例】

171.93
508.00

【分析】

        给定一个载重为M的背包,及n个重量为wi、价值为pi 的物体,要求把物品装满背包,且使背包内的物体价值最大,这类问题称为背包问题。有两类背包问题,即物体可以分割的背包问题,及物体不可分割的背包问题,我们把后者称为0/1背包问题,由0/1背包还可以延伸出很多背包问题。前者为可分割的背包问题,可以用贪心算法求解。贪心的策略是拿性价比高的物体。这一点也好理解,我们去超市免费拿东西,你挑最贵的拿,拿走一台冰箱,显然是傻蛋。如果物品可以分割,你当然应该去拿性价比高的物品,冰箱固然贵重,但其性价比并不贵,你应该直接奔金银首饰专柜去拿,金项链、翡翠这些都是性价比高的东西。

        以样例为例,我们来看求解过程。步骤为三步,分别是:

(1)求性价比

信息学奥赛一本通(1225:金银岛)

(2)按性价比,从大到小排序

信息学奥赛一本通(1225:金银岛)

(3)拿走最大价值

        从前到后物品开始拿,拿的策略是先拿性价比高的。如果当前背包重量 > 某个物品的总重量,那别客气,全拿走;否则,当前背包不足以拿走某个物品,那就能拿多少拿多少。样例数据,第一组,背包承重50,则先把第一个物品(性价比为10的)全拿走,价值=100,背包重量=40。同理,再把第二个物品全拿走,价值=134,背包重量=33,拿第三个,不足以全拿走,只能拿33,价值=134+33*1.15=171.93

信息学奥赛一本通(1225:金银岛)

【参考代码】

#include<stdio.h>
#include<string.h>
#define N 110
struct goods
{
	int n;     //总重量 
	double v;  //总价值 
	double p;  //性价比 
}gds[N],tmp;

int main()
{
	int t,i,j;
	int w;           //口袋承重上限 
	int s;           //金属种类 
	double sum=0;    //最多带走的价值 
	scanf("%d",&t);
	while(t--)
	{
		sum=0;
		memset(gds,0,sizeof(gds));

		scanf("%d%d",&w,&s);
		for(i=0;i<s;i++)
		{ 
			scanf("%d%lf",&gds[i].n,&gds[i].v);
			gds[i].p = gds[i].v / gds[i].n;
		}

		for(i=0;i<s-1;i++)      // 贪心策略是选性价比大的,按照性价比排序
		{
			for(j=i+1;j<s;j++)
		    {
		    	if(gds[i].p<gds[j].p)
				{
					tmp=gds[i];
					gds[i]=gds[j];
					gds[j]=tmp;
				}
			}
		}
		for(i=0;i<s;i++)       //枚举每件物品
		{
			if(w>gds[i].n)        //背包承重大于某物品的总重量,全拿走 
			{
				sum+=gds[i].v;
				w-=gds[i].n;
			}
			else                //背包承重小于某物品的总重量,切割物品,能拿多少拿多少 
			{
				sum+=gds[i].p*w;
				w=0;
			}
		}
		printf("%.2lf\n",sum);
	}
	return 0;
}

http://ybt.ssoier.cn:8088/problem_show.php?pid=1225

上一篇:Setup Collision and Overlap Event


下一篇:1225:金银岛