Codeforces 293B

题目链接:http://acm.hust.edu.cn/vjudge/problem/38379

题目大意:

给出一个n*m的矩阵,其中一些格子填了数,一些格子没有。现要求将未填数的格子填上数字,使得矩阵从左上到右下(不允许向左或上走)的任意一条路径上没有两个格子数字相同,数字范围为1~K。 1<=n,m<=1000,1<=k<=10

分析:

首先一看,在(n+m-1)大于K时必定无解,这样就把n,m范围缩小到了10以内。

我们考虑搜索解决。但是纯暴搜时间不够,可以用两个剪枝优化:

(1)可行性剪枝,如果在当前格子填数时,剩下的颜色已经不够用则直接回溯。

(2)对称性剪枝:例如我们当前填到了某个格子,我们记录一下整个矩阵已使用的颜色,例如3,5都还没用,那么这个格子填上3或5,两者最终算出的方案数是相同的,这样我们就可以只计算3的方案数,5的直接加上3的就可以了。


代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define mp(x,y) make_pair(x,y)
#define bit(x,d) ((((x)&(1<<(d)))==0)?0:1)

using namespace std;

typedef long long LL;

const int maxn=10+2;
const LL MOD=1000000000+7;


int F[maxn][maxn],vis[maxn],a[maxn][maxn],n,m,k,x;

LL ans;

void search(int x,int y)
{
 if (x==(n+1)) 
 {
  ans++;
  if (ans==MOD) ans=0;
  return;
 }
 
 F[x][y]=F[x-1][y]|F[x][y-1]; //位运算计算当前格子已使用颜色
  
 int p=1,q=k,c=0;

 rep(i,1,k) if (bit(F[x][y],i-1)) c++;
 
 if (((n-x)+(m-y)+1)>(k-c)) return; //可行性剪枝

 LL sum=-1,pre;
 if (a[x][y]>0) p=q=a[x][y];
 
 rep(i,p,q)
  if (!(bit(F[x][y],i-1)))
  {
   if (!vis[i]) 
   {
    if (sum>=0) //对称性剪枝
    {
     ans+=sum;
     if (ans>MOD) ans-=MOD;
     continue;
    }
    pre=ans;
   }

   F[x][y]^=(1<<(i-1)),vis[i]++;

   if (y==m) search(x+1,1); else search(x,y+1);

   F[x][y]^=(1<<(i-1)),vis[i]--;

   if (!vis[i])
   {
    sum=(ans-pre)%MOD;
   }
  }
  
}

int main()
{
 scanf("%d%d%d",&n,&m,&k);

 if ((n+m-1)>k) {printf("%d\n",0);return 0;} //特判
 
 memset(F,0,sizeof(F));
 memset(vis,0,sizeof(vis));

 rep(i,1,n) 
  rep(j,1,m)
  {
   scanf("%d",&a[i][j]);
   if (a[i][j]>0) vis[a[i][j]]++; //记录颜色出现数
  }
 
 
 ans=0;

 search(1,1);
 
 printf("%I64d\n",ans);

 return 0;
}

另一种做法,在没有限制的图上搜索,最终用排列数来计算对称解。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define bit(x,d) ((((x)&(1<<(d)))==0)?0:1)

using namespace std;

typedef long long LL;

const int maxn=10+2,MOD=1000000000+7;

int n,m,K,a[maxn][maxn],p[maxn],F[maxn][maxn],num[maxn][maxn];

bool pused[maxn],aused[maxn];

LL ans;

void check()
{
 memset(p,0,sizeof(p));
 memset(pused,0,sizeof(pused));
 memset(aused,0,sizeof(aused));

 int color=0,Kc=K;

 rep(i,1,K) if (bit(F[n][m],i-1)) color++;
 
 rep(i,1,n) 
  rep(j,1,m) 
   if (a[i][j]) 
   {
    if ((pused[num[i][j]])||(aused[a[i][j]])) return;
    p[num[i][j]]=a[i][j];
    pused[num[i][j]]=1,aused[a[i][j]]=1;
    Kc--;
   }
 
 rep(i,1,K) if (pused[i]) color--;

 LL k=1;
 dep(i,Kc,Kc-color+1) k=(k*i)%MOD;

 ans+=k;
 if (ans>MOD) ans-=MOD;
}

void search(int x,int y,int maxc)
{
 if (x==(n+1)) {check();return;}
 
 F[x][y]=F[x-1][y]|F[x][y-1];

 rep(i,1,maxc)
  if (!bit(F[x][y],i-1))
  {
   num[x][y]=i,F[x][y]^=(1<<(i-1));
   if (y==m) search(x+1,1,min(K,max(i+1,maxc))); else search(x,y+1,min(K,max(i+1,maxc)));
   num[x][y]=0,F[x][y]^=(1<<(i-1));
  }
}

int main()
{
 scanf("%d%d%d",&n,&m,&K);
 
 if ((n+m-1)>K) {printf("%d\n",0);return 0;}

 rep(i,1,n) rep(j,1,m) scanf("%d",&a[i][j]);

 search(1,1,1);
 
 printf("%I64d\n",ans);
 return 0;
}

posted @ 2016-08-03 11:01  Krew  阅读(624)  评论(0编辑  收藏  举报