ZOJ 3209 Treasure Map【DancingLink】

ZOJ 3209 Treasure Map
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3372
大意:已知有一张n*m的图,p个矩形,问至少需要多少个矩形能够完全覆盖整幅图,
      要求不能出现重叠
分析:
  转化为精确铺盖问题,即将图拉成一条长度为n*m的链,在该图中找到每个矩形
  对应的覆盖点,用DancingLink计算最少需要点数即可。。

#include<stdio.h>

#include<string.h>

const int MAX_COLOUMN = 30*30+2;//最多出现列数

const int MAX_ROW = 500+2;//最多出现的行数



int cnt[MAX_COLOUMN];//cnt[i]统计第i列1的个数

int most,coloumn;

//跳舞链中的节点

struct Point

{

   int up,down,left,right;//上,下,左,右

   int coloumn;//该点所在的列标



}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;

}



void dfs(int k)//k为已经选中的行的数目

{

 int i,j;

 if(k>=most)return;

 if(node[coloumn].right == coloumn)//当前跳舞链已为空

 {

  if(k<most)

   most = k;

  return;

 }



 int t = coloumn+1;

 int c;

 //选取当前矩阵中1最少的列

 for(i=node[coloumn].right;i!=coloumn;i=node[i].right)

 {

  if(cnt[i]<t)

  {

   c=i;t=cnt[i];

   if(t==1)break;

  }

 }

    

 remove(c);//删除列c中所有1所在的行



 //删除时从左到右从上到下,还原时从下到上,从右到左

 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);

  }

  dfs(k+1);

 

  for(j=node[j].left;j!=i;j=node[j].left)

  {

   resume(node[j].coloumn);

  }



  

 }



 resume(c);



}

bool graph[MAX_ROW][MAX_COLOUMN];

int wide;//地图的宽度

inline void addrow(int r,int x1,int y1,int x2,int y2)

{

   int i,j;

   for(i=x1;i<x2;i++)

    for(j=y1;j<y2;j++)

    {

     graph[r][j*wide+i]=true;

    }

}



char str[MAX_ROW];

int main()

{

 int N,M,i,j;

     int T;

  while(scanf("%d",&T)!=EOF)

  while(T--)

  {

   //printf("%s",str);

    int nn,mm,pp;

    scanf("%d%d%d",&nn,&mm,&pp);

    wide = nn;

    N=pp;

    M=mm*nn;

    coloumn = M;

   int cur=coloumn+1;//当前节点编号

   init(coloumn);

   memset(graph,0,sizeof(graph));

   for(i=0;i<pp;i++)//将图信息放入graph中

   {

    int x1,y1,x2,y2;

    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

    addrow(i,x1,y1,x2,y2);//添加该行状态

   }





   for(i=0;i<N;i++)//在跳舞链中插入图中非空位置的点

   {

    int start = cur;//记录第i列的开始点编号

    int pre = cur;//记录该列中当前1的左边第一个1编号

    for(j=0;j<M;j++)

    {

    // scanf("%d",&n);

     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;

      pre=cur++;

     }

    }

   }



  

   most = N+1;//记录最少需要选中的行数

   dfs(0);

      if(most==N+1)

             most=-1;   

    printf("%d\n",most);

  }

 

 return 0;

}

你可能感兴趣的:(link)