AcWing 337. 扑克牌

大型补档计划

题目链接

把状态实质相同的划分为一类...

发现花色、具体牌值的多少均不影响方案,考虑等效转化题目。

(f[A][B][C][D][k]) A 个 1 张相同,B 个 2 张相同,C 个 3张相同,D个 4张相同的牌,上一个放的牌现在有 (k) 张相同牌值的牌,排成的方案
状态转移就是考虑下一个放啥
((true) = 1 (false) = 0;)
考虑放 A 类
(f[A - (k == 0)][B][C][D][0])
考虑放 B 类
(2 * (B - (k == 1)) * f[A + 1][B - 1][C][D][1])
考虑放 C 类
(3 * (C - (k == 2)) * f[A][B + 1][C - 1][C][D][2])
考虑放 D 类
(4 * (D - (k == 3)) * f[A][B][C + 1][D - 1][C][D][3])

初始 (f[0][0][0][0][k] = 1;)

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef unsigned long long LL;
const int N = 14;
LL f[N][N][N][N][5];
int n, cnt[N], tot[5];
LL dp(int A, int B, int C, int D, int k) {
	if (f[A][B][C][D][k]) return f[A][B][C][D][k];
	if (!A && !B && !C && !D) return 1;
	LL &v = f[A][B][C][D][k] = 0;
	if (A) v += (A - (k == 1)) * dp(A - 1, B, C, D, 0);
	if (B) v += 2 * (B - (k == 2)) * dp(A + 1, B - 1, C, D, 1);
	if (C) v += 3 * (C - (k == 3)) * dp(A, B + 1, C - 1, D, 2);
	if (D) v += 4 * (D - (k == 4)) * dp(A, B, C + 1, D - 1, 3);
	return v;

}
int inline get(char c) {
	if (c == 'T') return 10;
	else if(c == 'J') return 11;
	else if(c == 'Q') return 12;
	else if(c == 'K') return 13;
	else if(c == 'A') return 1;
	else return c - '0';
}
int main() {
	int T; scanf("%d", &T);
	for (int Case = 1; Case <= T; Case++) {
		memset(cnt, 0, sizeof cnt);
		tot[0] = tot[1] = tot[2] = tot[3] = tot[4] = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) {
			char s[3]; scanf("%s", s);
			cnt[get(s[0])]++;
		}
		for (int i = 1; i <= 13; i++) tot[cnt[i]]++;
		printf("Case #%d: %llu
", Case, dp(tot[1], tot[2], tot[3], tot[4], 0));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/dmoransky/p/12380622.html