你有一些扑克牌,有一些出牌方式,问最少出多少张牌才能全部打光。
分别有:王炸、炸弹、单张、对子、三张、三带一、三带二、四带两张、四带两对、单顺子、双顺子、三顺子。
顺子不包含大小王和2。
没有飞机真是可惜,欢乐斗地主的飞机很爽的说
UOJ中途加强数据QwQ。
嘛,考虑搜索。
(还记得考场上的程序写的很丑)
显然对于一个出牌的方案,顺序是无关的,因此考虑优先出顺子,顺子出的牌最多是原因之一。
dfs搜索出顺子的方案。
那么对于每种出顺子的方案,我们可以知道还剩哪些牌,本着多出牌的原则,考虑四张和三张的牌带其他1张2张的牌出掉即可:依次出掉四带两对、四带两张、三带二、三带一,最后再出单种牌(1~4张)。
当然按照这个顺序出牌就不会出现某种牌有4张但一次只出掉3张的情况,带的对子和单牌也是,即每种牌只会一次全部出完。
然后贪心无法处理(感觉是这样?)顺子,所以必须搜顺子。
然后就没啥了。
最优性剪枝是照例有的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define FORD(i,j,k) for(int i=j;i>=k;i--)
const int card[] = {0, 4, 2, 1};
int a[16], b[16], ans;
int sum() {
#define p(i,x,j,y) while (b[i] >= x && b[j] >= y) b[i] -= x, b[j] -= y, ++s
int s = 0;
memset(b, 0, sizeof b);
FOR(i,0,14) if (i != 1) ++b[a[i]];
p(4, 1, 2, 2); p(4, 1, 1, 2); p(3, 1, 2, 1); p(3, 1, 1, 1);
return s + b[4] + b[3] + b[2] + b[1];
}
void dfs(int dep) {
if (dep >= ans) return;
int j;
FORD(x,3,1) FOR(i,3,13) {
for (j = i; j <= 14 && a[j] >= x; j++);
int len = (--j) - i + 1;
if (len > card[x]) {
FOR(k,i,i+card[x]-1) a[k] -= x;
FOR(k,i+card[x],j) a[k] -= x, dfs(dep + 1);
FOR(k,i,j) a[k] += x;
}
}
ans = min(ans, dep + sum());
}
int main() {
int t, n, x, y;
scanf("%d%d", &t, &n);
while (t--) {
memset(a, 0, sizeof a);
FOR(i,1,n) {
scanf("%d%d", &x, &y);
if (x == 1) x = 14;
++a[x];
}
ans = min(n, 13);
dfs(0);
printf("%d\n", ans);
}
return 0;
}