[SCOI2008] 着色方案

[SCOI2008] 着色方案(待续)

题目大意:一排\(n\)个木块,\(k\)种油漆,每种油漆有\(c_i\)桶,要求相邻两个木块的颜色不能相同,求着色方案\(\pmod{1e9+7}\)

Solution.1

  • 状态:设\(f[i][j]\)为前\(i\)块被涂色的方案,且最后一块是\(j\)颜色
  • 转移:$f[i][j] = \sum_{h=1,h \ne j } ^ {k}f[i-1][h] $

好的,记录不了每桶用了多少,挂掉。

诶,这个\(c[i]\)有点小啊,而且如果两种油漆剩下相同的桶数,说明

  • 状态:设\(f[a][b][c][d][e][last]\)为还剩\(1\)桶的有\(a\)种,还剩\(2\)桶的有\(b\)\(\dots\),还剩\(5\)桶的有\(e\)种,最后一个是上一次还剩\(k​\)桶的油漆的方案数。
  • 转移:详见代码

Code.1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

typedef long long ll;

const int Mod = 1e9 + 7;

int x[6];
ll f[16][16][16][16][16][6];

ll dp(int a, int b, int c, int d, int e, int k){
	if(a + b + c + d + e == 0) return 1;//都用光了
	if(f[a][b][c][d][e][k]) return f[a][b][c][d][e][k] % Mod;//记搜
	ll t = 0;
	if(a)
		t = (t + 1LL * (a - (k == 2)) * dp(a - 1, b, c, d, e, 1)) % Mod;
	if(b)
		t = (t + 1LL * (b - (k == 3)) * dp(a + 1, b - 1, c, d, e, 2)) % Mod;
	if(c)
		t = (t + 1LL * (c - (k == 4)) * dp(a, b + 1, c - 1, d, e, 3)) % Mod;
	if(d)
		t = (t + 1LL * (d - (k == 5)) * dp(a, b, c + 1, d - 1, e, 4)) % Mod;
	if(e)
		t = (t + 1LL * e * dp(a, b, c, d + 1, e - 1, 5)) % Mod;
	f[a][b][c][d][e][k] = t % Mod;//假设上一次用的是可以涂k个格子的油漆,那么上一次用完后,那种油漆就只可以涂k - 1个格子了,那么这一次涂油漆的时候就不能再选可以涂k - 1个格子的油漆的一种,因为这种上次已经用过了,且相同的油漆不能相邻。
	return t % Mod;
}

int main(){
	int k;
	scanf("%d", &k);
	for(int i = 1, la; i <= k; ++i){
		scanf("%d", &la);
		x[la] ++;
	}
	printf("%lld\n", dp(x[1], x[2], x[3], x[4], x[5], 0) % Mod);
	return 0;
}

Solution.2

排列组合搞\(DP\)

  • 状态:\(f[i][j]\)表示使用了前\(i\)种油漆,有\(j\)个相邻的同色快的方案数

考虑新加一种油漆\(i+1\),我们假设有\(x\)个,我们先将他们分组。

设分成了\(q\)组,用的插板和插空思想,有\(q-1\)个板子,有\(x-1\)个空,那么方案数就是\(C_{x-1}^{q-1}\).

并会产生\(x-q\)个相邻的色块,因为如果不分组,那么会产生\(x-1\)个相邻色块,分成\(q\)组,会减少\(q-1\)个相邻色块(手动画一画),所以最终产生的相邻色块是\((x-1)-(q-1)=x-q\)

下一步我们要把分好的组插入原来的木块中间(包括两边),所以总共就有\(sum[i]+1\)个空,其中有\(j\)个空两边颜色相同,我们用这\(q\)组中的\(b\)个插入左右颜色相同的位置,\(C_{j}^{b}\)就是插入到之前同色的之间的方案数(位置不同),\(C_{sum[i]+1−j}^{q−b}\)就是相当于前面有\(sum[i]+1\)个位置,\(j\)个相邻块占据的位置不能放,\(q-b\)插入进去的方案数

最后剩下的相邻色块就是\(j-b+(x-q)​\)

\[f[i + 1][j - b + (x - q)] = C_{x-1}^{q-1}\cdot C_{sum[i]+1−j}^{q−b} \cdot C_j^b\cdot f[i][j] \]

Code.2

#include <bits/stdc++.h>

using namespace std;

const int N = 106;
const int P = 1e9 + 7;

int k;
int a[N], sum[N];
int c[N][N];
int f[N][N];

inline void pre_work() {
	c[0][0] = 1;
	for(int i = 1; i <= 75; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j) 
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
	}
	return;
}

int main() {
	scanf("%d", &k);
	for(int i = 1; i <= k; ++i) {
		scanf("%d", &a[i]);
		sum[i] = sum[i - 1] + a[i];
	}
	pre_work();
	f[1][a[1] - 1] = 1;//a[1] - 1 是最多对数 
	for(int i = 1; i < k; ++i)
		for(int j = 0; j < sum[i]; ++j) {
			if(!f[i][j]) continue;
			for(int qwq = 1; qwq <= a[i + 1]; ++qwq)//最多能分成多少组
				for(int b = 0; b <= qwq && b <= j; ++b){//从qwq里面调不大于j的b个插到之前色块相邻的地方
					int res = 1LL * f[i][j] * c[a[i + 1] - 1][qwq - 1] % P * c[j][b] % P;
					res = 1LL * c[sum[i] + 1 - j][qwq - b] % P * res % P;//剩下的
					f[i + 1][j + a[i + 1] - qwq - b] = (f[i + 1][j + a[i + 1] - qwq - b] + res) % P;
				} 
		} 
	printf("%d\n", f[k][0]);
	return 0;
}

2018-09-24 14:39

posted @ 2018-08-31 11:15  LMSH7  阅读(198)  评论(0编辑  收藏  举报