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)求性价比
(2)按性价比,从大到小排序
(3)拿走最大价值
从前到后物品开始拿,拿的策略是先拿性价比高的。如果当前背包重量 > 某个物品的总重量,那别客气,全拿走;否则,当前背包不足以拿走某个物品,那就能拿多少拿多少。样例数据,第一组,背包承重50,则先把第一个物品(性价比为10的)全拿走,价值=100,背包重量=40。同理,再把第二个物品全拿走,价值=134,背包重量=33,拿第三个,不足以全拿走,只能拿33,价值=134+33*1.15=171.93
【参考代码】
#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;
}