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