您的位置:首页 > 其它

【UKIEPC2015 G】【DP】Drink Responsibly 买酒喝 双关键字DP恰好都用完 注意精度

2015-11-03 17:42 316 查看
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=0,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
double mm1,mm2,cc1;
int n,m1,m2;
char s[10][24];
int c1,c2,siz;
int f[1005][605];
int C1[10],C2[10];
int num[10];
void dfs(int y,int x)
{
if(y==0&&x==0)return;
int o=f[y][x];
++num[o];
dfs(y-C1[o],x-C2[o]);
}
int main()
{
while(~scanf("%lf%lf%d",&mm1,&mm2,&n))
{
MS(num,0);
m1=mm1*100+0.1;
m2=mm2*30;
MS(f,0);f[0][0]=1;
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]);
scanf("%d",&c2);
scanf("%*d");getchar();scanf("%d",&siz);
if(siz==1)c2*=30;
else if(siz==2)c2*=15;
else c2*=10;
scanf("%lf",&cc1);c1=cc1*100+0.1;
C1[i]=c1;
C2[i]=c2;
if(c1==0&&c2==0)continue;//特判没有意义的情况
for(int j=c1;j<=m1;j++)
{
for(int k=c2;k<=m2;k++)if(f[j][k]==0&&f[j-c1][k-c2])
{
f[j][k]=i;
}
}
}
if(f[m1][m2]==0)puts("IMPOSSIBLE");
else
{
dfs(m1,m2);
for(int i=1;i<=n;i++)if(num[i])printf("%s %d\n",s[i],num[i]);
}
}
return 0;
}
/*
【trick&&吐槽】
1,不要太畏惧double
有些情况下我们是通过完全与int相同的做法做
有些情况下我们是可以把double转化为int的

2,DP常常要考虑到拓扑序和环。
对于这题,如果有初始m1=m2=0,那么答案一定是yes
而如果对于一瓶酒,有c1=c2=0,那么这瓶酒我们不需要考虑啦(本题没有这个数据233)

3,编译器问题导致了,如果一个小数乘一个倍数,很有可能是0.99999999这样的数然后被认定为0。
所以像是+0.1这样维护精度的细节一定要注意

【题意】
去喝酒!!!
我们初始有m1(double,[0,10],小数点后最多两位)的钱,想把这钱恰好花光。
而且想喝到的酒精量恰为m2(double,[0,20],小数点后最多两位)。
有n种酒,数量都无限,对于每种酒,含有以下属性:
1,名称。(长度不超过20的字符串)
2,一整瓶的酒精含量。([0,100]范围内的int)
3,所对应的价格。(double,小数点后最多只有2位)

问你是否能够达到这个"双恰好的要求"

【类型】
DP

【分析】
首先,这道题的类型看起来像是一个DP。
但是,因为这题给出的下标可能为double!
不过,因为小数点后最多只有2位,我们可以乘上一个倍数,将其转换为整数。

对于钱数,*100即可
对于酒精量,我们首先是有必要*10的,同时我们还要解决1/2,1/3的情况。
于是我们可以对目标酒精量*60,对每瓶的酒精量分别*60,*30,*20,
或者我们可以对目标酒精量*30,对每瓶的酒精量分别*30,*15,*10,(后者可以使得效率提高一倍)

然后做个DP就可以啦。记录前驱的酒,用dfs输出一下方案即可。

【时间复杂度&&优化】
O(nm)

【数据】
input
10.00 9.0 2
fire 2 1/1 4.00
water 10 1/2 2.00

output
fire 2
water 1

input
2.00 3.0 3
firewater 1 1/1 1.00
windwater 1 1/1 1.00
earthwater 1 1/1 1.00

output
IMPOSSIBLE
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM ICPC 算法 DP