【NOIP2015】斗地主 D1 T3 及 增强版 (送命题)

恶心送命模拟题
暴搜顺子,DP预处理剩下的。 由于官方数据太水,很多情况没有讨论的都能过普通版本,想要测试自己代码正确性的同学们可以交交这道题,有很多dalao给出了hack数据 : Luogu P2540 斗地主 增强版 传送门

具体细节见代码注释
AC code:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 25;
int n, cnt[15], p[5], Ans, dp[6][8][12][24];
// 14 A
// 0 king

template<class T>inline bool chkmin(T &x, T y) { return y < x ? x = y, 1 : 0; }

inline int solve(int i, int j, int k, int w, int king)
{
	if(king == 0) return dp[i][j][k][w];
	if(king == 1) return dp[i][j][k][w+1]; // 单打
	return min(dp[i][j][k][w] + 1,  dp[i][j][k][w+2]); //王炸 + 单打
}

int need[4] = { 0, 5, 3, 2 };

void dfs(int now)
{
	if(now >= Ans) return;
	memset(p, 0, sizeof p); //注意清零
	for(int i = 2; i <= 14; i++) p[cnt[i]]++; //统计 4 3 2 1 个的
	chkmin(Ans, now + solve(p[4], p[3], p[2], p[1], cnt[0]));
	for(int len = 1; len <= 3; len++) //枚举顺子每个数字出现次数
		for(int i = 3, j; i <= 14; i++) //枚举起点
		{
			for(j = i; j <= 14 && cnt[j] >= len; j++) //枚举终点
			{
				cnt[j] -= len;
				if(j - i + 1 >= need[len]) dfs(now+1);
			}
			while(--j >= i) cnt[j] += len;
		}
}

void Pre() //DP预处理
{
	memset(dp, 0x3f, sizeof dp);
	for(int i = 0; i <= n/4; i++)
		for(int j = 0; j <= n/3; j++)
			for(int k = 0; k <= n/2; k++)
				for(int w = 0; w <= n; w++) if(i*4 + j*3 + k*2 + w <= n)
				{
					dp[i][j][k][w] = i + j + k + w;
					if(i)
					{
						if(w >= 2) chkmin(dp[i][j][k][w], dp[i-1][j][k][w-2] + 1);
						if(k >= 2) chkmin(dp[i][j][k][w], dp[i-1][j][k-2][w] + 1);
						chkmin(dp[i][j][k][w], dp[i-1][j][k][w] + 1); // 打 炸弹
						chkmin(dp[i][j][k][w], dp[i-1][j][k+2][w]); //拆
						chkmin(dp[i][j][k][w], dp[i-1][j][k+1][w+2]); //拆
						chkmin(dp[i][j][k][w], dp[i-1][j][k+1][w+2]); //拆
						chkmin(dp[i][j][k][w], dp[i-1][j][k][w+4]); //拆
						chkmin(dp[i][j][k][w], dp[i-1][j+1][k][w+1]); //拆
					}
					if(j)
					{
						if(w >= 1) chkmin(dp[i][j][k][w], dp[i][j-1][k][w-1] + 1);
						if(k >= 1) chkmin(dp[i][j][k][w], dp[i][j-1][k-1][w] + 1);
						chkmin(dp[i][j][k][w], dp[i][j-1][k][w] + 1); //打 3 个
						chkmin(dp[i][j][k][w], dp[i][j-1][k][w+3]); //拆
						chkmin(dp[i][j][k][w], dp[i][j-1][k+1][w+1]); //拆
					}
					if(k) chkmin(dp[i][j][k][w], dp[i][j][k-1][w] + 1), chkmin(dp[i][j][k][w], dp[i][j][k-1][w+2]); // 打 2 个 + 拆
					if(w) chkmin(dp[i][j][k][w], dp[i][j][k][w-1] + 1); //打 1 个
				}
}

int main ()
{
    int T, x, y;
	scanf("%d%d", &T, &n);
	Pre();
	while(T--)
	{
		memset(cnt, 0, sizeof cnt);
		
		for(int i = 1; i <= n; i++)
		{
			scanf("%d%d", &x, &y);
			if(x == 1) x = 14;// 把 A -> 14
			cnt[x]++;
		}
		Ans = n; dfs(0);
		printf("%d
", Ans);
	}
}
原文地址:https://www.cnblogs.com/Orz-IE/p/12039479.html