zoukankan      html  css  js  c++  java
  • 洛谷P2476 [SCOI2008]着色方案

    题目:https://daniu.luogu.org/problemnew/show/P2476

    题目背景

    四川NOI省选第二试

    题目描述

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

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1: 
    3
    1 2 3
    输出样例#1: 
    10
    输入样例#2: 
    5
    2 2 2 2 2
    输出样例#2: 
    39480
    输入样例#3: 
    10
    1 1 2 2 3 3 4 4 5 5
    
    输出样例#3: 
    85937576

    说明

    50%的数据满足:1 <= k <= 5, 1 <= ci <= 3

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

    解析

    这属于比较简单的省选dp吧。

    一开始看感觉下不去手,隐隐约约觉得是dp。

    当看到ci<=5时眼前一亮。

    这么小的范围,也许要有这么多维吧。

    然后就想到了中国象棋这道题https://www.luogu.org/problemnew/show/2051

    好了思路确立。

    先想一想dfs的打法。

    设a,b,c,d,e分别代表ci=1,ci=2,...,ci=5的颜色。

    那么ans=dfs(a,b,c,d,e,last(回头说用处))。

    我们考虑转移,

    a,b,c,d,e的状态可以从

    a-1,b,c,d,e

    a+1,b-1,c,d,e(假设用了ci=2的其中一个,那么这个ci=2就会变成ci=1,所以a要+1,后面同理)

    a,b+1,c-1,d,e

    a,b,c+1,d-1,e

    a,b,c,d+1,e-1

    状态转移过来。
     
    我们先单独考虑一下a,b,c,d,e转化到a+1,b-1,c,d,e的情况吧。
    这相当于用了ci=2的颜色一个。
    那么一共有几种可以用呢?
    题目要求颜色相同的不能靠在一块。
    我们设上次用的是last。
    此处要用ci=2的,
    如果上次用的是ci=3的,则所有ci=2中有一个颜色便是上次ci=3转移过来的。
    如果这次再用这个,则相同颜色在一块了,可选数-1。
    所以,dfs(a,b,c,d,e,last)转化成dfs(a+1,b-1,c,d,e,2)的贡献是:(b-(last==3))*dfs(a+1,b-1,c,d,e,2);
    其余同理,dfs思路确立。
    然后这个过程转化成记忆化即可。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 #define ll long long
     8 #define mod 1000000007
     9 ll k,re;
    10 ll c[6];
    11 ll f[16][16][16][16][16][6];
    12 ll dfs(ll a,ll b,ll c,ll d,ll e,ll last){
    13     if (f[a][b][c][d][e][last]) return f[a][b][c][d][e][last];
    14     ll tot=0;
    15     if (!a&&!b&&!c&&!d&&!e){
    16         f[0][0][0][0][0][last]=1;
    17         return 1;
    18     }
    19     if (a) tot=(tot+((a-(last==2))*dfs(a-1,b,c,d,e,1))%mod)%mod;
    20     if (b) tot=(tot+((b-(last==3))*dfs(a+1,b-1,c,d,e,2))%mod)%mod;
    21     if (c) tot=(tot+((c-(last==4))*dfs(a,b+1,c-1,d,e,3))%mod)%mod;
    22     if (d) tot=(tot+((d-(last==5))*dfs(a,b,c+1,d-1,e,4))%mod)%mod;
    23     if (e) tot=(tot+(e*dfs(a,b,c,d+1,e-1,5))%mod)%mod;
    24     return f[a][b][c][d][e][last]=tot;
    25 }
    26 int main(){
    27     scanf("%lld",&k);
    28     for (int i=1;i<=k;++i){
    29         scanf("%lld",&re);
    30         c[re]++;
    31     }
    32     printf("%lld",dfs(c[1],c[2],c[3],c[4],c[5],233666));
    33     return 0;
    34 }
    35                     
    View Code
  • 相关阅读:
    大数的四则运算
    整数划分问题(递归法)
    浅谈C++中内存分配、函数调用和返回值问题
    栈的模拟运用 SOJ3897 dance2
    C/C++:sizeof('a')的值为什么不一样?
    浅谈C++中指针和引用的区别
    n!的分解 soj 2666
    char *a 和char a[] 的区别(指针和数组的区别)
    错排公式的推导
    浅谈C语言中的指针
  • 原文地址:https://www.cnblogs.com/gjc1124646822/p/7899419.html
Copyright © 2011-2022 走看看