题目
题目链接:https://www.luogu.com.cn/problem/CF293B
小首有一个\(n\times m\)的木板,一些块已经被涂上给出的\(k\)种颜色中的一种。你需要把每个没涂色的块涂色使得从左上角到右下角的每条路径都不会经过两个颜色一样的块。路径只能向右或向下走。
输出答案\(\%1000000007\)。
思路
显然,一个\(n\times m\)的矩阵的路径长度为\(n+m-1\),那么\(n\times m\)的矩阵可以被\(k\)种颜色覆盖且每条路径均没有重复颜色的必要条件是\(n+m-1\leq k\)。
而这道题\(k\leq 10\),所以其实这个矩阵最大就只有\(30\)格(\(5\times 6\))。
那么就爆搜,需要加一下两个剪枝:
- 如果剩余格子比可以用的颜色多,直接返回。
- 如果一个颜色是第一次使用,那么如果再这个阶段有\(k\)种颜色是第一次使用,那么显然每一个第一次使用的颜色的方案数是一样的,直接\(\times k\)即可。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=35,M=15,MOD=1000000007;
int n,m,k,ans,a[N][N];
bool used[N][N][M],vis[M];
int dfs(int x,int y)
{
if (x==n+1) return 1;
int s=0;
for (int i=1;i<=k;i++)
{
used[x][y][i]=used[x-1][y][i]|used[x][y-1][i];
if (!used[x][y][i]) s++;
}
if (s<n-x+m-y+1) return 0;
int cnt=0,ans=0,cpy=-1;
for (int i=1;i<=k;i++)
{
if (a[x][y] && a[x][y]!=i) continue;
if (!used[x][y][i])
{
used[x][y][i]=1;
if (!vis[i])
{
if (cpy<0)
{
vis[i]=1;
if (y<m) cpy=dfs(x,y+1);
else cpy=dfs(x+1,1);
vis[i]=0;
}
ans=(ans+cpy)%MOD;
}
else if (y<m) ans=(ans+dfs(x,y+1))%MOD;
else ans=(ans+dfs(x+1,1))%MOD;
used[x][y][i]=0;
}
}
return ans;
}
int main()
{
//freopen("0.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
if (n+m-1>k) return !printf("0");
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
vis[a[i][j]]=1;
}
printf("%d\n",dfs(1,1));
return 0;
}