[SCOI2008]着色方案
题目地址:
基本思路:
必须吐槽一下,怎么题目又没有写数据范围QAQ,这题没数据范围写不了啊。
考虑,但是如果我们用每种颜色的油漆作为维度显然不太行,
因此我们关注,考虑到每一种油漆,如果能涂的木块的数量相同的话,其实是等效的,
所以我们用分别表示还剩下次使用次数的油漆的数量为,上一次我们涂的是使用次数剩余为的油漆,然后注意我们每次转移时因为不能连续两块相同颜色,所以如果上一次用的还能涂次的油漆,那么当前就有一种还能够涂次的油漆无用。
由于并没有明显的顺序递推关系,所以我们考虑用记忆化搜索模拟,具体转移方程比较长,见代码。
参考代码:
#pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; #define IO std::ios::sync_with_stdio(false) #define int long long #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, l, r) for (int i = l; i >= r; i--) #define mset(s, _) memset(s, _, sizeof(s)) #define pb push_back #define pii pair <int, int> #define mp(a, b) make_pair(a, b) #define INF (int)1e18 inline int read() { int x = 0, neg = 1; char op = getchar(); while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); } while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); } return neg * x; } inline void print(int x) { if (x < 0) { putchar('-'); x = -x; } if (x >= 10) print(x / 10); putchar(x % 10 + '0'); } const int mod = 1e9 + 7; int dp[16][16][16][16][16][6];// 使用次数分别为 1,2,3,4,5的油漆的数量,和上一次使用的是还能用几次的油漆; int dfs(int a,int b,int c,int d,int e,int nxt) { if (dp[a][b][c][d][e][nxt] != 0) return dp[a][b][c][d][e][nxt]; // 记忆化搜索过程,计算过就直接返回答案; if (a + b + c + d + e == 0) return dp[a][b][c][d][e][nxt] = 1; int res = 0; // 转移方程里前面括号中减去的那部分,是为了去除了连续两块颜色相同的情况; if (a > 0) res += 1ll * (a - (nxt == 2)) * dfs(a - 1, b, c, d, e, 1) % mod; res %= mod; if (b > 0) res += 1ll * (b - (nxt == 3)) * dfs(a + 1, b - 1, c, d, e, 2) % mod; res %= mod; if (c > 0) res += 1ll * (c - (nxt == 4)) * dfs(a, b + 1, c - 1, d, e, 3) % mod; res %= mod; if (d > 0) res += 1ll * (d - (nxt == 5)) * dfs(a, b, c + 1, d - 1, e, 4) % mod; res %= mod; if (e > 0) res += 1ll * e * dfs(a, b, c, d + 1, e - 1, 5) % mod; return dp[a][b][c][d][e][nxt] = res % mod; } int memo[6]; // 每种使用次数的油漆的数量; signed main() { int k = read(); rep(i,1,k) { int x = read(); memo[x]++; } int ans = dfs(memo[1],memo[2],memo[3],memo[4],memo[5],0); cout << ans << '\n'; return 0; }