洛谷 P2668 斗地主
题意简述
读入一组牌,求最少需要多少次出牌可以将它们打光。
题解思路
暴力搜索+剪枝
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, t, ax, bx, card[20], now, ans;
void dfs();
void shunzi(int x, int y);
int main()
{
scanf("%d%d", &t, &n);
for (int i = 1; i <= t; ++i)
{
ans = n;
memset(card, 0, sizeof(card));
for (int j = 1; j <= n; ++j)
{
scanf("%d%d", &ax, &bx);
if (ax == 0)
ax = 16;
if (ax < 3)
ax += 13;
card[ax] ++;
}
dfs();
printf("%d\n", ans);
}
}
void getstra(int x, int y)
{
for (int i = 3; i <= 15 - y; ++i)
{
int cnt = 0;
for (int j = i; j <= 14; ++j)
if (card[j] >= x)
cnt ++;
else break;
if (cnt >= y)
{
for (int j = i; j <= i + cnt - 1; ++j)
card[j] -= x;
dfs();
for (int j = i; j <= i + cnt - 1; ++j)
card[j] += x;
}
}
}
void dfs()
{
int cnt = 0;
for (int i = 3; i <= 15; ++i)
cnt += (card[i] + 3) / 4;
cnt += (card[16] + 1) / 2;
if (now >= ans)
return;
ans = min(ans, cnt + now);
now ++;
getstra(3, 2);
getstra(2, 3);
getstra(1, 5);
for (int i = 3; i <= 15; ++i)
{
if (card[i] >= 4)
{
card[i] -= 4;
for (int j = 3; j <= 16; ++j)
for (int k = 3; k <= 16; ++k)
{
if ((j - i & i - k & k - j) && card[j] == card[k] && card[j] && card[k])
{
card[j] --;
card[k] --;
dfs();
if (card[j] && card[k])
{
card[j] --;
card[k] --;
dfs();
card[j] ++;
card[k] ++;
}
card[j] ++;
card[k] ++;
}
}
card[i] += 4;
}
}
for (int i = 3; i <= 15; ++i)
{
if (card[i] >= 3)
{
card[i] -= 3;
for (int j = 3; j <= 16; ++j)
if ((j - i) && card[j])
{
card[j] --;
dfs();
if (card[j])
{
card[j] --;
dfs();
card[j] ++;
}
card[j] ++;
}
card[i] += 3;
}
}
now --;
}