这题的难点在于状态的设计
首先显然是个状压,需要一维表示卡的状态,另一维如果设计成天数,难以知道当前的钱数,没法确定是否能够购买新的卡,如果设计成钱数,会发现状态数过多,空间与时间都无法承受。但是可以发现,如果没有买卡的钱会因当前卡数变化而变化这个条件的话,买卡的钱是一定的,而我们因拥有卡而省的钱不会超过120(1+2+3+...+15)。所以可以将状态设计成f[i][j]表示卡的状态为i,省了j个红币,能省多少个蓝币。
然后就结束了...
注意所有下标为状态的数组的大小T T...
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> using namespace std; const int maxn=20, inf=1e9; int n, st, sumr, sumb, ans; int r[maxn], b[maxn], cntr[1<<16], cntb[1<<16], f[1<<16][121]; char ch[maxn][2]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<'0' || c>'9') c=='-'&&(f=-1), c=getchar(); while(c<='9' && c>='0') k=k*10+c-'0', c=getchar(); k*=f; } int main() { read(n); st=(1<<n)-1; for(int i=1;i<=n;i++)scanf("%s", ch[i]), read(r[i]), read(b[i]), sumr+=r[i], sumb+=b[i]; for(int i=0;i<=st;i++) for(int j=1;j<=n;j++) if(i&(1<<(j-1))) cntr[i]+=(ch[j][0]=='R')?1:0, cntb[i]+=(ch[j][0]=='B')?1:0; memset(f,-1,sizeof(f)); f[0][0]=0; for(int i=0;i<=st;i++) for(int j=0;j<=120;j++) if(f[i][j]!=-1) for(int k=1;k<=n;k++) if(!(i&(1<<(k-1)))) { int numr=min(r[k], cntr[i]), numb=min(b[k], cntb[i]); f[i|(1<<(k-1))][j+numr]=max(f[i|(1<<(k-1))][j+numr], f[i][j]+numb); } ans=inf; for(int i=0;i<=120;i++) if(f[st][i]!=-1) ans=min(ans, max(sumr-i, sumb-f[st][i])); printf("%d\n", ans+n); }