09 harbin现场赛 DLX

/*
0代表可以填任意数
e代表只能是奇数
o代表只能填偶数
相同字母的格子必须是同一个数
e,o,0是同样处理的,只不过e,o加入奇数或偶数,0加入全部的1-9
检查符合上述要求的解是否符合同字母同数字即可

*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int Row=9;
const int MAX_COLOUMN = Row*Row*4+2;//最多出现列数
const int MAX_ROW = Row*Row*Row+2;//最多出现的列数
int res;
int cnt[MAX_COLOUMN];//cnt[i]统计第i列1的个数
int most,coloumn;
bool ans[MAX_ROW];//ans存放最终选中的行
//跳舞链中的节点
struct Point
{
   int up,down,left,right;//上,下,左,右
   int coloumn;//该点所在的列标
   int row;//行标
}node[MAX_ROW*MAX_COLOUMN+MAX_COLOUMN];

void init(int m)
{
 int i;
 for(i=0;i<=m;i++)
 {
  node[i].down=i;
  node[i].up = i;
  node[i].coloumn=i;
  node[i].left=i-1;
  node[i].right=i+1;
  cnt[i]=0;
 }
 node[0].left = m;
 node[m].right = 0;
}

void remove(int c)//删除c列上所有1元素所在的行
{
 node[node[c].right].left=node[c].left;
 node[node[c].left].right=node[c].right;
 int t,tt;
 for(t=node[c].down;t!=c;t=node[t].down)//从上到下从左到右删除该列上的每一非零元素所在行信息
 {
  for(tt = node[t].right;tt!=t;tt=node[tt].right)//删除非零元素所在行
  {
            cnt[node[tt].coloumn]--;
   node[node[tt].down].up = node[tt].up;
   node[node[tt].up].down = node[tt].down;
  }
 }
}

void resume(int c)//还原c列上所有1元素所在的行
{
 int t,tt;
 for(t=node[c].up;t!=c;t=node[t].up)//从下往上从左到右还原该c列中1所在的行信息
 {
  for(tt=node[t].left;tt!=t;tt=node[tt].left)
  {
   cnt[node[tt].coloumn]++;
   node[node[tt].up].down=tt;
   node[node[tt].down].up=tt;
  }
 }

 node[node[c].right].left=c;
 node[node[c].left].right=c;
}
int tmp[9][9];
typedef pair<int,int> pii;
vector<pii> Join[26];//把同字母的坐标放入同一个数组中
void dfs(int k)//k为已经选中的行的数目
{
 int i,j;
 //if(k>=most)return ;
 if(node[coloumn].right == coloumn)//当前跳舞链已为空
 {
    for(int k=0;k<Row;k++)
    {
              for(i=0;i<Row;i++)
              {
               for(j=0;j<Row;j++)
                if(ans[(k*Row+i)*Row+j])
                 tmp[k][i]=j+1;
             }
   
    }
    int flag=1;
    for(int i=0;i<26;i++)
    {
        for(int j=1;j<Join[i].size();j++)
        {
            if(tmp[Join[i][j].first][Join[i][j].second]!=tmp[Join[i][j-1].first][Join[i][j-1].second])
            {
                flag=0;
                break;
            }
        }
    }
    if(flag){
        res++;};

  return ;
 }

 int t = coloumn+1;
 int c;
 for(i=node[coloumn].right;i!=coloumn;i=node[i].right)
 {
  if(cnt[i]<t)
  {
   c=i;t=cnt[i];
  }
 }
 remove(c);
 for(i = node[c].down;i!=c;i=node[i].down)
 {
  for(j=node[i].right;j!=i;j=node[j].right)
  {
   remove(node[j].coloumn);
  }
  ans[node[j].row]=true;
  dfs(k+1);
  ans[node[j].row]=false;
  for(j=node[j].left;j!=i;j=node[j].left)
  {
   resume(node[j].coloumn);
  }
 }
 resume(c);
 return ;
}
bool graph[MAX_ROW][MAX_COLOUMN];
void addrow(int i,int j,int k)
{
    int curr = (i*Row+j)*Row+k;
 graph[curr][(i*Row+j)]=true;
 graph[curr][Row*Row+i*Row+k]=true;
 graph[curr][Row*Row*2+j*Row+k]=true;
 int tmp=(int)(sqrt(Row));
 int tr = i/tmp;
 int tc = j/tmp;
 graph[curr][Row*Row*3+(tr*tmp+tc)*Row+k]=true;
}

char str[Row][Row];

int main()
{
 int N,M,i,j,k,ca,nu;
 scanf("%d",&ca);
	 while(ca--)
	 {
        res=0;
        for(int i=0;i<26;i++)
        Join[i].clear();
	  for(i=0;i<Row;i++)
	  {
	  scanf("%s",str[i]);
	  }
	  N=Row*Row*Row;
	  M = Row*Row*4;
	  coloumn = M;
	  int cur=coloumn+1;//当前节点编号
	  init(coloumn);
	  memset(graph,0,sizeof(graph));
	  for(i=0;i<Row;i++)
	   for(j=0;j<Row;j++)
	   {

		 if(str[i][j]=='o')
		{
            for(k=0;k<Row;k=k+2)//遍历每一种颜色
            {
		   addrow(i,j,k);
            }
		}
		else if(str[i][j]=='e')
		{
		     for(k=1;k<Row;k=k+2)//遍历每一种颜色
            {
		     addrow(i,j,k);
            }
		}
		else if(str[i][j]>='1'&&str[i][j]<='9')
		{
		    addrow(i,j,str[i][j]-'1');
		}
		else
		{
		    if(str[i][j]!=0)
		    {
		        Join[str[i][j]-'a'].push_back(make_pair(i,j));
		    }
             for(k=0;k<Row;k++)//遍历每一种颜色
             {
               addrow(i,j,k);
             }
		}
    }
	  for(i=0;i<N;i++)
	  {
	   int start = cur;//记录第i列的开始点编号
	   int pre = cur;//记录该列中当前1的左边第一个1编号
	   for(j=0;j<M;j++)
	   {
		if(graph[i][j])//跳舞链中仅插入非0元素
		{
		 int pos = j;
		 node[cur].up = node[pos].up;
		 node[node[pos].up].down = cur;
			node[cur].down = pos;
		 node[pos].up = cur;
		 cnt[pos]++;//该列1的个数+1
		 node[cur].coloumn = pos;
		 node[cur].left = pre;
		 node[pre].right = cur;
		 node[cur].right = start;
		 node[start].left=cur;
		 node[cur].row = i;
		 pre=cur++;
		}
	   }
	  }
	  most = N+1;//记录最少需要选中的行数
	  memset(ans,false,sizeof(ans));
	  dfs(0);
	  printf("%d\n",res);
	 }
 return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值