CF293B Distinct Paths dfs+剪枝

【问题描述】
给定一个 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)求模的结果。

分析:
显然当 n+m1>k n + m − 1 > k 时,答案就是 0 0 。对于剩下的部分我们可以暴力搜索。因为一个点颜色不能与他左上角矩形内的任意颜色相同,所以可以使用一个二进制状态f[i][j]来表示左上角矩形的颜色是否出现。因为 (i,j) ( i , j ) 还没有上色,如果剩余颜色总数小于 ni+mj+1 n − i + m − j + 1 ,这样就剪掉了。还有就是对偶剪枝,即两种未出现颜色的方案数是相同的,可以理解为每一种方案的前一种颜色变为后一种颜色,后一种颜色变为前一种颜色。但是如果都出现了一次就不能这样剪枝了,因为这两种颜色出现的位置不同,方案数就不同了。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const LL p=1e9+7;

using namespace std;

const int maxn=12;

int n,m,k;
int f[maxn][maxn],a[maxn][maxn],num[maxn];

LL dfs(int x,int y)
{
    if (y>m)
    {
        x++;
        y=1;
    }
    if (x>n) return 1;
    f[x][y]=f[x][y-1]|f[x-1][y];
    int c=0;
    for (int i=f[x][y];i>0;i-=i&(-i)) c++;
    if (n-x+m-y+1>k-c) return 0;
    LL ret=-1,sum=0;
    for (int i=((~f[x][y])&((1<<k)-1));i>0;i-=i&(-i))
    {
        int d=i&(-i);
        int t=trunc(log(d+0.5)/log(2))+1;
        if ((!a[x][y]) || (a[x][y]==t))
        {
            int pre=f[x][y];
            f[x][y]|=d;
            num[t]++;
            if (num[t]==1)
            {
                if (ret==-1) ret=dfs(x,y+1);
                sum=(sum+ret)%p;
            }
            else sum=(sum+dfs(x,y+1))%p;
            num[t]--;
            f[x][y]=pre;
        }
    }
    return sum;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);   
    if (n+m-1>k)
    {
        printf("0");
        return 0;
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            if (a[i][j]) num[a[i][j]]++;
        }
    }   
    LL ans=dfs(1,1);    
    printf("%lld",ans);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值