才艺
描述
说起才艺,YH最为得意的就是他的吃鸡之道。为了将其精湛的技艺传授给ACM的其他人,YH决定每天从机房带走一个人…..去吃鸡。YB经过千年的等待,终于等来了吃鸡的机会。正准备跨出机房之时,YB想起了一个关键的问题。这个吃鸡,到底是谁买单呢?于是YB便把他的疑虑跟YH提了出来。为了体现公平公正性,YH提出了一个游戏,让YB玩。如果YB能赢,则自己买单,否则就是YB买单。
游戏规则如下:
有n堆硬币,每堆有Ai个硬币(1=<i<=n),,每堆硬币中有一个假币,假币比真币要重。YH给YB一个天平秤,YB可以通过使用秤来找到其中的假币,每次秤只能称同一堆的硬币。而且YH约定,每堆硬币中的假币价值为Vi(1=<i<=n),YB每找出一个假硬币,就能获得其价值。如果YB能在使用k次秤之后,获的总价值不少于m,则算YB赢。
强大的YB经过计算发现,有些情况是自己一定能赢的(也就是在说最坏的情况下也能赢)。如果自己一定能赢,鉴于YH良好的人品,YB会主动提出自己买单,然后一起去吃鸡。否则的话,也会因其不良的人品,不想与其共修此道,打算自己参悟,一个人去吃鸡。
输入
第一行有三个整数n(1<=n<=1000),k(0<=k<=1000),m(0<=m<=10000),分别以空格隔开。
第二行有n个整数,分别代表Ai;
第三个有n个整数,分别代表Vi;
其中,Ai,Vi都属于int型。
输出
若YB一定能赢,则输出”YB YH CJ”,否则输出”YB CJ”;
样例输入
5 0 62
1 1 1 1 1
2 4 8 16 32
5 4 62
2 2 2 2 2
2 4 8 16 32
样例输出
YB YH CJ
YB CJ
题目来源
PHK
http://218.194.91.48/acmhome/problemdetail.do?&method=showdetail&id=1228
这道题就是简单的背包问题,只不过进行小小的变形。
很明显的是这个题目求的是至多要称几次(因为要确定YB必须要赢),并且每堆至少有一个硬币。
所以当ai==0的时候,就确定是一个假硬币。两个的话,就要称一次。三个的话,就称两次就可以,天平两段各放一个硬币,如果相等,则表明剩下的那个是假硬币。
否则,说明质量大的为假硬币。
也就是说把总硬币分为三堆,只有这样的情况下,称的次数会最小。
3^0+1~3^1 最少为1
3^1~3^2 最少为2
3^2~3^3 最少为3
.......
#include<iostream>
#include<stdio.h>
using namespace std;
int dp[10010],v[1010],c[1010];
struct node
{
int a,v;
}tot[1010];
int main()
{
int temp,i,j,n,k,z,m,sum,y;
while(~scanf("%d%d%d",&n,&k,&m))
{
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",&temp);
if(temp==1) tot[i].a=0;//用于cost的转化
else
{
y=0;
while(temp!=0)
{
temp/=3;
y++;
}
tot[i].a=y;
}
}
for(z=0,i=0;i<n;i++)
{
scanf("%d",&tot[i].v);
if(tot[i].a==0)
sum+=tot[i].v;
else //把不为0,转化为01背包的形式
{
c[z]=tot[i].a;
v[z++]=tot[i].v;
}
}
if(sum>=m) cout<<"YB YH CJ"<<endl;//如果不用称就确定YB能赢,就直接输出 "YB YH CJ"
else
{
memset(dp,0,sizeof(dp));
for(i=0;i<z;i++)//简单的01背包
for(j=k;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
if(dp[k]+sum>=m) cout<<"YB YH CJ"<<endl;
else cout<<"YB CJ"<<endl;
}
}
return 0;