[CF293B] Distinct Paths

问题描述

给定一个 n*m 的矩形色板,有 k 种不同的颜料,有些格子已经填上了某种颜色,现在 需要将其他格子也填上颜色,使得从左上角到右下角的任意路径经过的格子都不会出现两种 及以上相同的颜色。路径只能沿着相邻的格子,且只能向下或者向右。 计算所有可能的方案,结果对 1000000007 (10^9 + 7)求模。

输入格式

第一行,三个整数 n, m, k (1 ≤ n, m ≤ 1000, 1 ≤ k ≤ 10); 接下来 n 行,每行包含 m 个整数,表示颜色。其中 0 表示未涂色,非 0 表示颜色的编号, 颜色编号为 1 到 k。

输出格式

一行,一个整数,表示涂色方案对 1000000007 (10^9 + 7)求模的结果。

样例输入

2 2 4
0 0
0 0

样例输出

48

解析

首先,从(1,1)到(n,m)的路径长度为n+m-1,那么如果颜色种类数小于n+m-1,无论如何都不存在解。所以,我们把n和m的范围缩小到了10。

观察条件:从左上角到右下角的任意路径经过的格子都不会出现两种及以上相同的颜色。既然是任意路径,那么不难得到推论,一个格子的左上方不能出现和自己相同的颜色。而颜色种类只有10,不妨用状压的方法储存一个点左上角出现过的颜色集合。具体的,设\(f[i][j]\)表示(i , j)左上角出现过的颜色集合,则
\[ f[i][j]=f[i-1][j]\ \ | \ \ f[i][j-1] \]
还有一个剪枝。如果在某个格子上,某种颜色是第一次出现,那么这个格子上其他第一次出现的颜色得到的方案数和这个颜色是相同的,不必重复计算。

代码

#include <iostream>
#include <cstdio>
#define N 1002
using namespace std;
const int mod=1000000007;
int n,m,k,i,j,a[N][N],vis[12],col[N][N];
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
int count(int x)
{
    int ans=0;
    for(int i=0;(1<<i)<=x;i++){
        if(x&(1<<i)) ans++;
    }
    return ans;
}
int dfs(int x,int y)
{
    if(y==m+1) x++,y=1;
    if(x==n+1) return 1;
    int s=(col[x-1][y]|col[x][y-1])^((1<<k)-1),ans=0,tmp=-1;
    if(n-x+m-y+1>count(s)) return 0;
    for(int i=0;(1<<i)<=s;i++){
        if(s&(1<<i)&&(a[x][y]==0||a[x][y]==i+1)){
            col[x][y]=(col[x][y-1]|col[x-1][y]|(1<<i));
            vis[i+1]++;
            if(vis[i+1]==1){
                if(tmp==-1) tmp=dfs(x,y+1);
                ans=(ans+tmp)%mod;
            }
            else ans=(ans+dfs(x,y+1))%mod;
            vis[i+1]--;
        }
    }
    return ans;
}
int main()
{
    n=read();m=read();k=read();
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            a[i][j]=read();
            vis[a[i][j]]++;
        }
    }
    if(n+m-1>k) puts("0");
    else printf("%d\n",dfs(1,1));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LSlzf/p/11789897.html