CodeForces 1051d:连通块 DP
Posted zarax
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces 1051d:连通块 DP相关的知识,希望对你有一定的参考价值。
题面大意:有2*N的格子,每个格子可以涂成白色或者黑色,问有k个连通块的涂色方案数
N<=103,k<=2*103
我的错误思路:我没看懂题目对不起呜呜呜...
正解思路:
首先看到N<=103,反正凭我的感觉,一般来说103或者104这样的,很可能是DP或者记忆化,也就是N2的复杂度,100的话可能是dfs啊这样的爆搜,105可能就是nlogn或者n*o(1)了。
这是题外话,首先看一下,如果我要DP或者记忆化的话,那么首先要构建状态。复盘的话,我觉得那个N就是个暗示,也就是去以列为状态,结合最容易想到的从左向右推,那么第一个状态就是i列。光有列肯定不够,第二个状态就用题目中现成的状态:连通块。那么j代表连通块数目。然后尝试一下能不能推?我们发现,如果从左向右的话,当前最右边一列的组合是什么样是对新加进来组合后多出的连通块数目有影响(就是说,如果我要添加进来一个11,那么最后一列什么样对这个11加进来后,多出了多少个连通块是有影响的。)
那么也就有了第三个状态:最后一列的组合(正好只有4个,多了可能就开不下了)
有了状态,那么下面来看转移:
首先我要说一下我是选择了递推,如果是DP那样通过i-1这样的之前状态来确定当前状态,那么可能会访问越界...
转移其实就是一句话:枚举。
枚举什么?枚举最后一列长什么样,和新加进来那列长什么样,因为只有他们之间的关系对于新方案数有影响。
具体怎么枚举,详见代码~
#include <cstdio> typedef long long int LL; LL MOD=998244353,N,K,ans; LL dp[1005][2005][5];//用1234分别表示00,01,10,11四种状态 int main(){ scanf("%d%d",&N,&K); dp[1][1][1]=1;//dp[i][j][k]代表 i列,有j个连通块,最后一列状态为k的矩阵的填色方案数 dp[1][2][2]=1; dp[1][2][3]=1; dp[1][1][4]=1; for(int i=1;i<=N;i++){ for(int j=1;j<=2*N;j++){ for(int k=1;k<=4;k++)dp[i][j][k]%=MOD;//别忘了% dp[i+1][j][1]+=dp[i][j][1];/*如果最后一列是1,新加进来一个1,那么连通块数目不会变*/ dp[i+1][j+1][2]+=dp[i][j][1];/*如果最后一列是1,新家进来一个2,那么这是连通块数目多了一个,后面以此类推*/ dp[i+1][j+1][3]+=dp[i][j][1]; dp[i+1][j+1][4]+=dp[i][j][1]; dp[i+1][j][1]+=dp[i][j][2]; dp[i+1][j][2]+=dp[i][j][2]; dp[i+1][j+2][3]+=dp[i][j][2]; dp[i+1][j][4]+=dp[i][j][2]; dp[i+1][j][1]+=dp[i][j][3]; dp[i+1][j+2][2]+=dp[i][j][3]; dp[i+1][j][3]+=dp[i][j][3]; dp[i+1][j][4]+=dp[i][j][3]; dp[i+1][j+1][1]+=dp[i][j][4]; dp[i+1][j+1][2]+=dp[i][j][4]; dp[i+1][j+1][3]+=dp[i][j][4]; dp[i+1][j][4]+=dp[i][j][4]; } } for(int i=1;i<=4;i++) ans+=dp[N][K][i]; printf("%lld",ans%MOD); return 0; }
以上是关于CodeForces 1051d:连通块 DP的主要内容,如果未能解决你的问题,请参考以下文章
CodeForces - 1051D Bicolorings(DP)
Codeforces 1051 D.Bicolorings(DP)
Educational Codeforces Round 87 (Rated for Div. 2) E. Graph Coloring(dp)