题目大意
给你一个n*m的网格,现在让你往里面填1~k(有的位置已经填了),使其满足所有从(1,1)到(n,m)的路径不会经过相同的数字(只能往下或往右),求方案数
解题思路
对于k<n+m-1的,无法填满任意一条路径,特判掉
然后考虑dfs
每个位置只填左上矩阵和右下矩阵没填过的数(保证不重),且如果当前位置剩余颜色小于到终点的距离,那么直接特判
对于自由填的颜色(输入没填过),考虑到某些方案只是将颜色转换了一下,那么让自由填的颜色从小到大填,然后乘排列数
code
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 12
#define wyc 1000000007
using namespace std;
ll n,m,k,ls,ans,num,g[N],a[N][N],p[N],nx[N];
bitset<N>sum[N][N],summ[N][N];
void dfs(ll x,ll y,ll now,ll gg)
{if(x>n||y>m){(ans+=g[gg])%=wyc;;return;}if(a[x][y]){if(summ[x-1][y][a[x][y]]||summ[x][y-1][a[x][y]]||sum[x+1][y][a[x][y]]||sum[x][y+1][a[x][y]])return;summ[x][y]=summ[x-1][y]|summ[x][y-1];summ[x][y].set(a[x][y],1);if(n-x+m-y>k-summ[x][y].count())return;dfs(x+y/m,y%m+1,now,gg);return;}for(ll i=1;i<=k;++i)if(p[i]||i<=now){if(summ[x-1][y][i]|| summ[x][y-1][i]||sum[x+1][y][i]||sum[x][y+1][i])continue;summ[x][y]=summ[x-1][y]|summ[x][y-1];summ[x][y].set(i,1);if(n-x+m-y>k-summ[x][y].count())return;if(i==now)dfs(x+y/m,y%m+1,nx[now],gg+1);else dfs(x+y/m,y%m+1,now,gg);}return;
}
int main()
{scanf("%lld%lld%lld",&n,&m,&k);if(n+m-1>k){puts("0");return 0;}for(ll i=1;i<=n;++i)for(ll j=1;j<=m;++j)scanf("%lld",&a[i][j]);for(ll i=n;i>0;--i)for(ll j=m;j>0;--j)if(a[i][j]){sum[i][j]|=sum[i][j+1]|sum[i+1][j];if(sum[i][j][a[i][j]]){puts("0");return 0;}sum[i][j].set(a[i][j],1);p[a[i][j]]=1;}ls=k+1;for(ll i=k;i>0;--i)if(!p[i]){num++;nx[i]=ls;ls=i;}g[0]=1;for(int i=1;i<=num;++i)g[i]=g[i-1]*(num-i+1);dfs(1,1,ls,0);printf("%lld",ans);return 0;
}