题意
斗地主。
问你最少几次能把所有牌出完。
具体出牌规则详见各大 OJ 。
思路
很明显,先把所有顺子暴力搜掉,然后处理带的问题。
我原本想直接贪心做带的问题,但是非常麻烦。考虑到牌数非常少,可以直接记录一个状态 f c 1 , c 2 , c 3 , c 4 , c 0 f_{c1,c2,c3,c4,c0} fc1,c2,c3,c4,c0 分别表示有 1,2,3,4 张牌的牌种的数量和王的数量。
然后记忆化搜索就好了。注意一种有多张的牌可能会被拆开成多张散牌被别人带,这是比较坑的点。然后注意不要忘记王。
考察细心。
代码
#include<bits/stdc++.h>
using namespace std;
int T, n, cnt[15], tmp[5], f[24][13][8][6][3], ans;
void chkMin(int &x, int y){if (y < x) x = y;}
int dp(int c1, int c2, int c3, int c4, int c0){
if (f[c1][c2][c3][c4][c0] != -1) return f[c1][c2][c3][c4][c0];
int ret = n;
if (!c1 && !c2 && !c3 && !c4 && !c0) ret = 0;
if (c1) chkMin(ret, dp(c1-1, c2, c3, c4, c0)+1);
if (c2){
chkMin(ret, dp(c1+2, c2-1, c3, c4, c0));
chkMin(ret, dp(c1, c2-1, c3, c4, c0)+1);
}
if (c3){
chkMin(ret, dp(c1+3, c2, c3-1, c4, c0));
chkMin(ret, dp(c1+1, c2+1, c3-1, c4, c0));
if (c2) chkMin(ret, dp(c1, c2-1, c3-1, c4, c0)+1);
if (c1) chkMin(ret, dp(c1-1, c2, c3-1, c4, c0)+1);
if (c0) chkMin(ret, dp(c1, c2, c3-1, c4, c0-1)+1);
chkMin(ret, dp(c1, c2, c3-1, c4, c0)+1);
}
if (c4){
chkMin(ret, dp(c1+1, c2, c3+1, c4-1, c0));
chkMin(ret, dp(c1, c2+2, c3, c4-1, c0));
chkMin(ret, dp(c1, c2, c3, c4-1, c0)+1);
if (c2 >= 2) chkMin(ret, dp(c1, c2-2, c3, c4-1, c0)+1);
if (c1 >= 2) chkMin(ret, dp(c1-2, c2, c3, c4-1, c0)+1);
if (c0 >= 2) chkMin(ret, dp(c1, c2, c3, c4-1, c0-2)+1);
if (c1 && c0) chkMin(ret, dp(c1-1, c2, c3, c4-1, c0-1)+1);
}
if (c0) chkMin(ret, dp(c1, c2, c3, c4, 0)+1);
return f[c1][c2][c3][c4][c0] = ret;
}
void dfs(int now, int stp){
if (stp >= ans) return;
if (now == 4){
memset(tmp, 0, sizeof tmp);
for (int i = 2; i <= 14; ++ i)
tmp[cnt[i]]++;
tmp[0] = cnt[0];
stp += dp(tmp[1], tmp[2], tmp[3], tmp[4], tmp[0]);
chkMin(ans, stp);
return;
}
else if (now == 1){ // 三顺
for (int i = 3; i <= 13; ++ i){
for (int j = i; j <= 15; ++ j){
if (cnt[j] < 3 || j == 15){
for (int k = i; k < j; ++ k) cnt[k] += 3;
break;
}
cnt[j] -= 3;
if (j-i+1 >= 2) dfs(now, stp+1);
}
}
}
else if (now == 2){ // 双顺
for (int i = 3; i <= 12; ++ i){
for (int j = i; j <= 15; ++ j){
if (cnt[j] < 2 || j == 15){
for (int k = i; k < j; ++ k) cnt[k] += 2;
break;
}
cnt[j] -= 2;
if (j-i+1 >= 3) dfs(now, stp+1);
}
}
}
else if (now == 3){ // 单顺
for (int i = 3; i <= 10; ++ i){
for (int j = i; j <= 15; ++ j){
if (cnt[j] < 1 || j == 15){
for (int k = i; k < j; ++ k) cnt[k]++;
break;
}
cnt[j]--;
if (j-i+1 >= 5) dfs(now, stp+1);
}
}
}
dfs(now+1, stp);
}
int main()
{
memset(f, -1, sizeof f);
for (scanf("%d%d", &T, &n); T--; ){
memset(cnt, 0, sizeof cnt);
ans = n;
for (int i = 1; i <= n; ++ i){
int x, y;
scanf("%d%d", &x, &y);
if (x == 1) x = 14;
cnt[x]++;
}
dfs(1, 0);
printf("%d\n", ans);
}
return 0;
}