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

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

  • 题意:有\(n\)个木块排成一行,从左到右依次编号为\(1\)~\(n\)。你有\(k\)种颜色的油漆,其中第\(i\)种颜色的油漆足够涂\(c_i\)个木块。所有油漆刚好足够涂满所有木块,即\(\sum\limits _{i=1}^{k}c_i=n\)。统计任意两个相邻木块颜色不同的着色方案。(\(1 \le k \le 15\) ,\(1\le 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\n", Dp(a[1], a[2], a[3], a[4], a[5], 0) );
    return 0;
}

原文地址:https://www.cnblogs.com/zjp-shadow/p/8436712.html

时间: 2024-08-28 08:33:49

BZOJ 1079: [SCOI2008]着色方案(巧妙的dp)的相关文章

BZOJ 1079: [SCOI2008]着色方案 DP

1079: [SCOI2008]着色方案 Description 有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案. Input 第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck. Output 输出一个整数,即方案总数模1,000,000,007的结果. Sample Inp

BZOJ 1079: [SCOI2008]着色方案 记忆化搜索

1079: [SCOI2008]着色方案 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1079 Description 有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的

bzoj 1079: [SCOI2008]着色方案

1 #include<cstdio> 2 #include<iostream> 3 #define M 1000000007 4 using namespace std; 5 int f[16][16][16][16][16][6],ma[16][16][16][16][16][6],a[6],k; 6 long long dp(int a1,int a2,int a3,int a4,int a5,int a6) 7 { 8 if(a1+a2+a3+a4+a5==0) 9 retu

bzoj1079: [SCOI2008]着色方案(dp)

1079: [SCOI2008]着色方案 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2133  Solved: 1287[Submit][Status][Discuss] Description 有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案.

bzoj1079: [SCOI2008]着色方案

ci<=5直接想到的就是5维dp了...dp方程YY起来很好玩...写成记忆化搜索比较容易 #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #

[SCOI2008] 着色方案[高维dp]

321. [SCOI2008] 着色方案 ★★★   输入文件:color.in   输出文件:color.out   简单对比时间限制:1 s   内存限制:64 MB 题目背景: 有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i 种颜色的油漆足够涂ci 个木块.所有油漆刚好足够涂满所有木块,即 c1+c2+...+ck=n.相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案.[输入]第一行为一个正整数k,第二行包含k个整数c1, c2,

【BZOJ 1079】[SCOI2008]着色方案

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 Out

【状态表示】Bzoj1096 [SCOI2008] 着色方案

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 Out

洛谷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. 输出