这类题...真的写不动T T
首先可以发现没有顺子的话出牌次数是一定的, 换句话说只有顺子会影响出牌次数。
所以可以暴搜出所有顺子的方案, 搜完之后记忆化搜索求一下a张1张同色牌, b张2张同色牌,c张3张同色牌, d张4张同色牌的最少出牌次数, 注意搜索的时候b可以拆出2个a, c可以拆出1个a, 2个b, d可以拆除2个b或者1个a, 1个c, 然后剩下的直接搜就好了, 这个效率不会估T T
所以求可行方案用记忆化搜索也是很快的...效率大概就是状态数...

#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=25, inf=1e9; int T, n, x, y; int f[maxn][maxn][maxn][maxn], cnt[maxn], cntsum[maxn]; int least[4]={0, 5, 3, 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 dfs(int a, int b, int c, int d) { if(~f[a][b][c][d]) return f[a][b][c][d]; int ans=inf; if(b) ans=min(ans, dfs(a+2, b-1, c, d)); if(c) ans=min(ans, dfs(a+1, b+1, c-1, d)); if(d) ans=min(ans, min(dfs(a+1, b, c+1, d-1), dfs(a, b+2, c, d-1))); if(a && c) ans=min(ans, dfs(a-1, b, c-1, d)+1); if(b && c) ans=min(ans, dfs(a, b-1, c-1, d)+1); if(a>=2 && d) ans=min(ans, dfs(a-2, b, c, d-1)+1); if(b>=2 && d) ans=min(ans, dfs(a, b-2, c, d-1)+1); return f[a][b][c][d]=min(ans, a+b+c+d); } int solve(int step) { int ans=inf; for(int i=1;i<=3;i++) for(int j=3;j<=14;j++) { int len=0; for(int k=j;k<=14;k++) if(cnt[k]>=i) len++; else break; for(int k=j+least[i]-1;k<=j+len-1;k++) { for(int l=j;l<=k;l++) cnt[l]-=i; ans=min(ans, solve(step+1)); for(int l=j;l<=k;l++) cnt[l]+=i; } } cntsum[1]=cntsum[2]=cntsum[3]=cntsum[4]=0; for(int i=2;i<=14;i++) cntsum[cnt[i]]++; if(cnt[0]==2) ans=min(ans, step+1+dfs(cntsum[1], cntsum[2], cntsum[3], cntsum[4])); cntsum[1]+=cnt[0]; ans=min(ans, step+dfs(cntsum[1], cntsum[2], cntsum[3], cntsum[4])); return ans; } int main() { memset(f, -1, sizeof(f)); f[0][0][0][0]=0; read(T); read(n); while(T--) { memset(cnt, 0, sizeof(cnt)); for(int i=1;i<=n;i++) read(x), read(y), cnt[x==1?14:x]++; printf("%d ", solve(0)); } return 0; }