题意:小明要买卡片,卡片分红、蓝两种,每天,小明可以: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;
}