• [SCOI2008]着色方案


    因为这类问题都需要从前一步来推后一步,所以大概率是DP类问题

    首先我们需要确定状态,如果把每种颜色都当成一维来记录的话,最大是(15) 维肯定是不可取的,所以就要考虑别的状态

    因为每种颜色(c_ileq5) ,我们可以把每种颜色剩余能涂的个数看成一个等价类 来确定每种状态

    (f[c1][c2][c3][c4][c5][lst]) 其中,(c1) 表示在所有颜色中剩余能涂的个数为(1) 的,有(c1) 个颜色(颜色之间有相同,也有不同)

    (c2,c3,c4,c5) 同理,(lst==last) 代表上一次用的是那种等价类里面的颜色

    • 如果把不同颜色,按照剩余能涂的个数来划分到一个等价类,会不会对答案造成影响?,不会

    • 因为我们每一步取的颜色,对于它来说,肯定会改变它的状态,所以每一步取的颜色肯定都不相同

    例如 当前有(4) 种颜色(a,b,c,d) 它们都剩余了(5) 个,所以 (c5==4) 这些颜色一共(20) 个 ,其中(c1,c2,c3,c4,c5) 是相互独立的

    我们从(c5) 中拿出一个颜色(可以不知道取的是那种颜色)之后(c5--)(c4++) 因为其中有一种颜色(我们不关心掉下去的颜色是谁,只关心这个等价类)从原先的剩余(5) 个变成了(4)

    这就是他们相互独立的原因,也是可以这样分类来取的原因。还要记得判断一下(lst)

    if(c2) res += (c2 - (lst == 3)) * dfs(c1 + 1,c2 - 1,c3,c4,c5,2);
    

    这段代码的意思是,如果(c2) 还能取,就把这种情况加到(res) 中,如果上次取的是(c3) 那么就会从(c3) 中掉一种颜色到(c2) 所以,我们要避开这种相同颜色,只能取剩下的(c2-1) 种了,取了(c2) 种后,下一步就是(c1++,c2--,lst=2) 然后根据乘法原理搞就行

    最后相加取模即可

    if(c5) res += c5 * dfs(c1,c2,c3,c4 + 1,c5 - 1,5);
    

    因为不可能有(c6) 所以就不用考虑(lst) 的影响了,直接搞

    #include <bits/stdc++.h> 
    using namespace std;
    const int N = 16,mod = 1e9 + 7;
    typedef long long LL;
    int f[N][N][N][N][N][6],k,c[N],x,n;
    LL dfs(int c1,int c2,int c3,int c4,int c5,int lst) {
        // 如果这种状态被记录过,直接返回即可,不用计算
        if(f[c1][c2][c3][c4][c5][lst]) return f[c1][c2][c3][c4][c5][lst];
        LL res = 0;
        if(c1) res += (c1 - (lst == 2)) * dfs(c1 - 1,c2,c3,c4,c5,1);
        if(c2) res += (c2 - (lst == 3)) * dfs(c1 + 1,c2 - 1,c3,c4,c5,2);
        if(c3) res += (c3 - (lst == 4)) * dfs(c1,c2 + 1,c3 - 1,c4,c5,3);
        if(c4) res += (c4 - (lst == 5)) * dfs(c1,c2,c3 + 1,c4 - 1,c5,4);
        if(c5) res += c5 * dfs(c1,c2,c3,c4 + 1,c5 - 1,5);
        return f[c1][c2][c3][c4][c5][lst] = res % mod;
    }
    
    int main() {
        // 边界条件
        for(int i = 1;i <= 5; ++i) f[0][0][0][0][0][i] = 1;
        cin >> n;
        for(int i = 1;i <= n; ++i) {
            cin >> x;// 读入第 i 种颜色的个数
            c[x] ++;// 能涂 i 个木块的颜色一共有 c[x] 个
        }
        cout << dfs(c[1],c[2],c[3],c[4],c[5],0) << endl;
        return 0;
    }
    
  • 相关阅读:
    从滴滴flinkCEP说起
    从零讲Java,给你一条清晰地学习道路!该学什么就学什么!
    从coalesce算子发散开的
    从架构理解价值-我的软件世界观
    为什么说Redis是单线程的?
    全排列_获取第几个排列_获取是第几个排列__康托展开,逆康托展开
    求排列是全排列的第几个,求第几个是全排列___康托展开,逆康托展开
    搜索入门_简单搜索bfs dfs大杂烩
    无根树同构_hash
    乘法逆元_三种方法
  • 原文地址:https://www.cnblogs.com/lukelmouse/p/13340026.html
Copyright © 2020-2023  润新知