Codeforces Round #385 (Div. 2) E. Hongcow Buys a Deck of Cards DP+好题

来源:互联网 发布:网络缓存服务器 编辑:程序博客网 时间:2024/04/28 17:18

题意:小明要买卡片,卡片分红、蓝两种,每天,小明可以:1.获得一个红币和一个蓝币,注意是和。2.买卡。

买卡需要的价格是max(ri-A,0)个红币和max(bi-B,0)个蓝币,ri、bi是第i张卡片所需红蓝币的个数,A、B是已经拥有的红蓝卡的个数。

求买完所有卡片所需的最少天数。

解法:

n很小,自然可以想到状态压缩dp,由于每天是同时获得一枚红币和蓝币,所以dp数组如果存天数会变得难以处理剩下的钱。

所以可以反过来想,dp数组存的是节省了多少钱。

dp[1<<16][121]:第一维代表集合:1代表已买,0代表未买。第二维代表省下的红币,由于省的钱<=120,开121足够。

dp[i][j]的值表示集合i状态下,省j枚红币时,最多能省的蓝币个数,最终答案就枚举一下dp[(1<<n)-1][j]更新一下答案就行。

代码中,预处理了ra和rb,分别表示某个集合红币和蓝币的个数。

#include <bits/stdc++.h>using namespace std;const int maxn=120;char op[16][2];int n,s,dp[1<<16][121],ta[16],tb[16],ra[1<<16],rb[1<<16],resa,resb,res,na,nb;int main(){    scanf("%d",&n);    s=1<<n;    for (int i=0;i<n;++i) {        scanf("%s%d%d",op[i],&ta[i],&tb[i]);        resa+=ta[i];        resb+=tb[i];    }    for (int i=0;i<s;++i)        for (int j=0;j<n;++j)            if (i&(1<<j)) {                ra[i]+=op[j][0]=='R'?1:0;                rb[i]+=op[j][0]=='B'?1:0;            }    memset(dp,-1,sizeof dp);    dp[0][0]=0;    for (int i=0;i<s;++i)        for (int j=0;j<=maxn;++j) {            if (dp[i][j]==-1)                continue;            for (int k=0;k<n;++k) {                if (i&(1<<k))                    continue;                na=min(ta[k],ra[i]);                nb=min(tb[k],rb[i]);                dp[i|(1<<k)][j+na]=max(dp[i|(1<<k)][j+na],dp[i][j]+nb);            }        }    res=max(resa,resb);    for (int i=0;i<=maxn;++i)        if (dp[s-1][i]!=-1)            res=min(res,max(resa-i,resb-dp[s-1][i]));    printf("%d\n",res+n);    return 0;}

0 0
原创粉丝点击