版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangjingyanzjyer/article/details/83152053
题目大意就是给了你n行m列的矩阵,让你给这个矩阵的每个格子染色,有1,2,3三种颜色可以选择。同时,必须满足任意两个相邻的格子的颜色不能相同。并且第k行已经完成了染色,无法被更改。求合法的染色方案数。
这道题n的范围很大,但m的范围很小,所以很容易想到可以使用状压dp来求解。由于每一个点的状态有三种,所以要用三进制数来表示每一行的状态。处理好这一点后,就照普通的状压dp做就可以了。不过还要注意判断一下题目给出的第k行的状态是否合法,不合法就直接输出0。(其实这道题的小数据理论上是要用暴力做的,直接dp空间会炸,不过LOJ的数据没有卡这一点)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const long long mo=1e6;
int n,m,k,x,d[1000],d0[1000],d1[1000],num,num0,num1,sta[100035];
long long ans,f[100035][835];
bool check(int x)//判断该状态是否合法
{
int cnt0=0;
while (x)
{
d0[++cnt0]=x%3;
x=x/3;
}
bool flag=true;
for (int i=m;i>=2;i--)
if (d0[i]==d0[i-1]) {flag=false;break;}
for (int i=1;i<=cnt0;i++) d0[i]=0;
return flag;
}
bool check0(int x,int y)//判断当前状态与上一行(或下一行)状态是否冲突
{
int cnt0=0;
while (x)
{
d0[++cnt0]=x%3;
x=x/3;
}
cnt0=0;
while (y)
{
d1[++cnt0]=y%3;
y=y/3;
}
bool flag=true;
for (int i=1;i<=m;i++)
if (d0[i]==d1[i]) {flag=false;break;}
for (int i=1;i<=m;i++){d0[i]=0;d1[i]=0;}
return flag;
}
void ready()
{
for (int i=0;i<d[m];i++)
{
if (check(i)==false || sta[1]==i) continue;
num++;
sta[num]=i;
}
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%d",&k);
memset(d,0,sizeof(d));
d[0]=1;
for (int i=1;i<=m;i++)
d[i]=d[i-1]*3;
num=1;
for (int i=1;i<=m;i++)
{
scanf("%d",&x);
sta[1]=(sta[1]*3)+x-1;//用0,1,2三进制数来表示状态
}
if (!check(sta[1])) {puts("0");return 0;}
ready();
if (k!=2)
{
for (int i=1;i<=num;i++)
f[1][sta[i]]=1;
}
else
{
for (int i=1;i<=num;i++) if (check0(sta[i],sta[1])) f[1][sta[i]]=1;
}
for (int i=2;i<=n;i++)
{
num1=num;
if (i==k) num1=1;
for (int j=1;j<=num1;j++)
{
num0=num;
if (i-1==k) num0=1;
for (int l=1;l<=num0;l++)
{
if (check0(sta[j],sta[l])==false) continue;
if ((i==k-1) && (check0(sta[j],sta[1])==false)) continue;
f[i][sta[j]]=(f[i][sta[j]]+f[i-1][sta[l]])%mo;
}
}
}
num0=num;
if (n==k) num0=1;
for (int i=1;i<=num0;i++) ans=(ans+f[n][sta[i]])%mo;
printf("%lld\n",ans);
}