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

题意

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

题解

特别巧妙的dp!一开始容易想到用cik时间复杂度做法QAQ,并没有什么用。

但是可以启发我们也许可以用kci算法去解决问题。然而我还是不会。。

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

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

所以就有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; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8436712.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(312)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示