JZOJ4012. 【CF293B】Distinct Paths

Description

小首有一个n*m的木板,一些块已经被涂上给出的k种颜色中的一种。你需要把每个没涂色的块涂色使得从左上角到右下角的每条路径都不会经过两个颜色一样的块。路径只能向右或向下走。
输出答案%1000000007。

Input

第一行3个整数n,m,k。
接下来n行,每行m个整数表示木板,其中第一行表示最上的m块,每行第一个数表示最左列的块。
对于每个整数,如果为0,表示未涂色,否则表示涂上颜色的编号。

Output

一个答案表示方案数。

Sample Input

输入1:
2 2 4
0 0
0 0
输入2:
2 2 4
1 2
2 1
输入3:
2 6 10
1 2 3 4 5 6
0 0 0 0 0 0
输入4:
5 6 10
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

Sample Output

输出1:
48
输出2:
0
输出3:
4096
输出4:
3628800

Data Constraint

对于40%的数据,1<=n,m<=3
对于100%的数据,1<=n,m<=1000,1<=k<=10

题解

看到数据范围就觉得很假,
k这么小,跟n,m完全不是一个数量级。
于是因为k这么小,这题完全可以暴力。

最关键的一个剪枝:对称性剪枝。
具体如下:假设要往当前这个格子填一种颜色x,
而且x这个颜色在此之前是没有出现过的,
同样,另外一种颜色y也是在此之前没有出现过的,
很显然,这里填x与填y的方案数是一样的,
于是就不必要再次递归了。

还有一些神奇的优化:改变搜索顺序。
先将每一行,每一列都填上一种颜色,
然后再把剩下的填完。
这样在每一行每一列都有颜色的情况下,限制条件会明显变多。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 100003
#define M 13
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,m,k,z[M],a[M][M],s[M][M],ans,r[M][M],c[M][M],num[M];

bool pd(int x,int y,int col)
{
    bool t=0;
    for(int i=1;i<=x;i++)
        t=t||(s[i][y]&z[col-1]);
    return t;
}

int dfs(int x,int y)
{
    int S=0,t=-123456789;
    if(y>m)x++,y=1;
    //printf("%d %d\n",x,y);
    if(x>n)return 1;
    if(a[x][y]==0)
    {
        for(int i=1;i<=k;i++)
        {
            if(c[x][i] || r[y][i] || pd(x,y,i))continue;
            c[x][i]=r[y][i]=1;a[x][y]=i;
            s[x][y]=s[x][y-1]|z[i-1];
            num[i]++;
            if(num[i]==1)
            {
                if(t<0)t=dfs(x,y+1);
                S+=t;
            }
            else S+=dfs(x,y+1);
            s[x][y]=c[x][i]=r[y][i]=a[x][y]=0;num[i]--;
        }
    }
    else 
    {
        if(pd(x,y,a[x][y]))return 0;
        s[x][y]=s[x][y-1]|z[a[x][y]-1];
        S+=dfs(x,y+1);
        s[x][y]=0;
    }
    return S;
}

int main()
{   
    read(n);read(m);read(k);
    if(n+m-1>k)
    {
        P('0');return 0;
    }
    z[0]=1;
    for(int i=1;i<M;i++)
        z[i]=z[i-1]<<1;

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            read(a[i][j]),c[i][a[i][j]]=r[j][a[i][j]]=1;
            if(a[i][j])num[a[i][j]]++;
        }

    printf("%d\n",dfs(1,1));

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/81088447
今日推荐