[SCOI2008] 着色方案

[SCOI2008] 着色方案(待续)

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

Solution.1

  • 状态:设(f[i][j])为前(i)块被涂色的方案,且最后一块是(j)颜色
  • 转移:$f[i][j] = sum_{h=1,h e 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
", 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^bcdot 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
", f[k][0]);
	return 0;
}

2018-09-24 14:39

原文地址:https://www.cnblogs.com/LMSH7/p/9564768.html