• 【BZOJ】1079: [SCOI2008]着色方案(dp+特殊的技巧)


    http://www.lydsy.com/JudgeOnline/problem.php?id=1079

    只能想到5^15的做法。。。。。。。。。。。。。。。。。。。。。。。。。。。果然我太弱。

    其实应该是没利用好题目的信息,ci<=5!

    那么我们可以将颜色所剩余的格子看做一种等价类!

    即,设状态f[a,b,c,d,e]表示还剩1个格子的颜色有a种,剩2个格子的颜色有b种...依次类推,且当前正在放第n-1*a-2*b-3*c-4*d-5*e+1格子。那么转移就是

    f[a,b,c,d,e]=a*f[a-1,b,c,d,e]+b*f[a+1,b-1,c,d,e]+c*f[a,b+1,c-1,d,e]+...+e*f[a,b,c,d+1,e-1]

    可是我们发现没有考虑相邻的情况?没事!我们可以加一维!

    我们再加一维,表示上一次用的颜色是等价类last,那么这一次计算的时候因为不能相邻,那么这个这一次放last-1的颜色时要少一个,所以是a-1或b-1或....或e-1然后再乘上后边的f。

    那么转移就变成了:

    f[a,b,c,d,e,last]=(a-(last==2))*f[a-1,b,c,d,e]+(b-(last==3))*f[a+1,b-1,c,d,e]+(c-(last==4))*f[a,b+1,c-1,d,e]+...+(e-(last==6))*f[a,b,c,d+1,e-1]

    而last==6无意义,可以去掉。

    那么记忆化搜索即可。

    真是一道好题!

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    #define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    
    const ll MD=1000000007;
    ll f[17][17][17][17][17][6];
    ll dp(int a, int b, int c, int d, int e, int last) {
    	if((a|b|c|d|e)==0) return 1;
    	if(f[a][b][c][d][e][last]) return f[a][b][c][d][e][last];
    	ll ret=0;
    	if(a) ret+=dp(a-1, b, c, d, e, 1)*(a-(last==2));
    	if(b) ret+=dp(a+1, b-1, c, d, e, 2)*(b-(last==3));
    	if(c) ret+=dp(a, b+1, c-1, d, e, 3)*(c-(last==4));
    	if(d) ret+=dp(a, b, c+1, d-1, e, 4)*(d-(last==5));
    	if(e) ret+=dp(a, b, c, d+1, e-1, 5)*e;
    	return f[a][b][c][d][e][last]=ret%MD;
    }
    
    int a[6];
    int main() {
    	int n=getint();
    	for1(i, 1, n) a[getint()]++;
    	printf("%lld
    ", dp(a[1], a[2], a[3], a[4], a[5], 0));
    	return 0;
    }
    

      


    Description

    有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。

    Input

    第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck。

    Output

    输出一个整数,即方案总数模1,000,000,007的结果。

    Sample Input

    3
    1 2 3

    Sample Output

    10

    HINT

     100%的数据满足:1 <= k <= 15, 1 <= ci <= 5

    Source

  • 相关阅读:
    StrictMode 检测应用
    动态设置视图大小
    查看手机CPU每个APP利用率
    获取屏幕尺寸,大于7为平板,小于7为手机
    Charles 抓取https 包
    SparseArray
    Gradle 差异化构建
    HashMap原理
    Gc root 定义
    Java多线程
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4137105.html
Copyright © 2020-2023  润新知