• BZOJ 1079: [SCOI2008]着色方案(巧妙的dp)


    题意

    (n)个木块排成一行,从左到右依次编号为(1)~(n)。你有(k)种颜色的油漆,其中第(i)种颜色的油漆足够涂(c_i)个木块。所有油漆刚好足够涂满所有木块,即(sumlimits _{i=1}^{k}c_i=n)。统计任意两个相邻木块颜色不同的着色方案。((1 le k le 15) ,(1le c_i le 5))

    题解

    特别巧妙的dp!一开始容易想到用({c_i}^k)时间复杂度做法QAQ,并没有什么用。

    但是可以启发我们也许可以用(k^{c_i})算法去解决问题。然而我还是不会。。

    我就看了一下别人的博客2333 发现dp很巧妙

    我们可以存储剩余能涂(q)个木块的油漆还剩多少种。这样时空复杂度就都降到(k^{c_i})了。

    所以就有dp[a][b][c][d][e]来记录答案(a,b,c,d,e分别表示1,2,3,4,5的种数),所以就有

    dp[a][b][c][d][e] = dp[a - 1][b][c][d][e] * a + dp[a + 1][b - 1][c][d][e] * b + dp[a][b + 1][c - 1][d][e] * c + dp[a][b][c + 1][d - 1][e] * d + dp[a][b][c][d + 1][e - 1] * e; (之间的+1,-1就是前面一种颜料从能涂q块,变成q-1了)

    但这并不符合题目要求(不然一个组合数就结束了),所以我们多记一个状态last表示上一次是用能涂last次的油漆涂的,如果这次我们用last - 1的话,就有一种颜料重复了,所以就要减去一种的贡献。

    这样就基本做完了,但dp顺序有点麻烦,所以就上记忆化吧,十分简短易写,强力安利!

    具体dp方程见程序吧。。不想写了QAQ

    代码

    /**************************************************************
        Problem: 1079
        User: zjp_shadow
        Language: C++
        Result: Accepted
        Time:752 ms
        Memory:67848 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
    #define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
     
    bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
       int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
        return x * fh;
    }
     
    void File () {
    #ifdef zjp_shadow
        freopen ("P1079.in", "r", stdin);
        freopen ("P1079.out", "w", stdout);
    #endif
    }
     
    const int N = 17, Mod = 1e9 + 7;
    typedef long long ll;
     
    ll dp[N][N][N][N][N][6];
     
    ll Dp(int a, int b, int c, int d, int e, int last) {
        if ((a | b | c | d | e) == 0) return 1;
        ll &res = dp[a][b][c][d][e][last];
        if (~res) return res; res = 0;
        if (a) res += Dp(a - 1, b, c, d, e, 1) * (a - (last == 2) );
        if (b) res += Dp(a + 1, b - 1, c, d, e, 2) * (b - (last == 3) );
        if (c) res += Dp(a, b + 1, c - 1, d, e, 3) * (c - (last == 4) );
        if (d) res += Dp(a, b, c + 1, d - 1, e, 4) * (d - (last == 5) );
        if (e) res += Dp(a, b, c, d + 1, e - 1, 5) * e;
        res %= Mod;
        return res;
    }
     
    int main () {
        File();
        int n = read(), a[6] = {0};
        For (i, 1, n) ++ a[read()];
        Set(dp, -1);
        printf ("%lld
    ", Dp(a[1], a[2], a[3], a[4], a[5], 0) );
        return 0;
    }
    
  • 相关阅读:
    MySQL之ERROR 1558 (HY000): Column count of mysql.user is wrong.解决方案
    手动发布本地jar包到Nexus私服
    Git的常用命令
    手游录屏直播技术详解 | 直播 SDK 性能优化实践
    【容器云】十分钟快速构建 Influxdb+cadvisor+grafana 监控
    【容器云】传统金融企业的 Docker 实践
    直播推流端弱网优化策略 | 直播 SDK 性能优化实践
    云存储之覆盖上传——七牛云
    「视频直播技术详解」系列之七:直播云 SDK 性能测试模型
    「视频直播技术详解」系列之六:现代播放器原理
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8436712.html
Copyright © 2020-2023  润新知