您的位置:首页 > 其它

POJ 3740 Easy Finding 详细讲解

2011-08-25 13:48 260 查看
[b]POJ 3740 Easy Finding

[/b]
[b]转载自http://hi.baidu.com/wangyu_cn/blog/item/69ad9e325a74b9a35edf0ecf.html

[/b]

精确覆盖问题
给定一个由0,1组成的矩阵,找到一个行的集合,使得这些行的每一列都有且只有一个1
可以用DLX解决,DLX 里的X就是X算法: http://en.wikipedia.org/wiki/Knuth's_Algorithm_X int remove(int a)

{

删除列a;

删除列a上为1的每一行;

}

int resume(int a)

{

remove的反操作;

}

int dfs()//X算法递归程序

{

if(矩阵为空) return 1;

找到1最少的列a;

remove(a)

for i=a列上的为1的每一行

{

remove(i行上为1的每一列);

if(dfs()==1) return 1;

resume(i行上为1的每一列);

}

resume(a);

return 0;

}
DL 是dancing links,是种用十字链表提高X算法效率的技巧,具体内容可去搜论文,或者直接看程序
或者看这个~ http://en.wikipedia.org/wiki/Dancing_Links 矩阵的十字链表 表示:
0 1 1 0
1 0 1 1
0 1 0 0
表示为



程序如下:
#include <vector>

#include <stdio.h>

#include <string.h>

using namespace std;

const int R=16,C=301;

int mat[R][C];

struct node

{

node *up,*down,*left,*right;

int i,j; //记录每个节点的行和列

}clo[C],row[R],head,all[R*C]; //图中 蓝色的节点,黄色的节点,红色的节点,紫色的节点

int num[C];//记录每列1的个数

void remove(int c)//删除一列和关联的行

{

if(c==-1) return ;//-1的列是row所在的列,不删除

clo[c].right->left=clo[c].left;

clo[c].left->right=clo[c].right;

for(node *ip=clo[c].down;ip!=&clo[c];ip=ip->down)

{

for(node *jp=ip->right;jp!=ip;jp=jp->right)

{

if(jp->j==-1) continue;//跳过-1的列

num[jp->j]--;

jp->up->down=jp->down;

jp->down->up=jp->up;

}

}

}

void resume(int c)//恢复

{

if(c==-1) return;

clo[c].right->left=&clo[c];

clo[c].left->right=&clo[c];

for(node *ip=clo[c].down;ip!=&clo[c];ip=ip->down)

{

for(node *jp=ip->right;jp!=ip;jp=jp->right)

{

if(jp->j==-1) continue;

num[jp->j]++;

jp->up->down=jp;

jp->down->up=jp;

}

}

}

int dfs()

{

if(head.right==&head) return 1;

node *minp; int min=1000000000;

for(node *ip=head.right;ip!=&head;ip=ip->right)

{

if(num[ip->j]<min)

{

min=num[ip->j];

minp=ip;

}

}//找到1最少的列

if(min==0) return 0;//如果有全为0的列,返回失败

remove(minp->j);

for(node *ip=minp->down;ip!=minp;ip=ip->down)

{

for(node *jp=ip->right;jp!=ip;jp=jp->right)

remove(jp->j);

if(dfs()==1) return 1;

for(node *jp=ip->left;jp!=ip;jp=jp->left)//恢复的顺序因该和删除的顺序相反

resume(jp->j);

}

resume(minp->j);

return 0;

}

void make(int m,int n)//生成十字链表

{

int i,j,k;

int newn=-1;

memset(num,0,sizeof(num));
head.left=&head;

head.right=&head;

head.up=&head;

head.down=&head;

head.i=-1;

head.j=-1;//初始化红色的节点

for(i=0;i<n;i++)

{

clo[i].i=-1;

clo[i].j=i;

clo[i].up=&clo[i];

clo[i].down=&clo[i];

clo[i].left=&head;

clo[i].right=head.right;

clo[i].left->right=&clo[i];

clo[i].right->left=&clo[i];

}//加入蓝色的节点
for(i=0;i<m;i++)

{

row[i].i=i;

row[i].j=-1;

row[i].left=&row[i];

row[i].right=&row[i];

row[i].up=&head;

row[i].down=head.down;

row[i].up->down=&row[i];

row[i].down->up=&row[i];

} //加入黄色的节点
for(i=0;i<m;i++)

{

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

{

if(mat[i][j]==0) continue;

newn++;

num[j]++;

all[newn].i=i;

all[newn].j=j;

all[newn].down=&clo[j];

all[newn].up=clo[j].up;

all[newn].up->down=&all[newn];

all[newn].down->up=&all[newn];

all[newn].right=&row[i];

all[newn].left=row[i].left;

all[newn].right->left=&all[newn];

all[newn].left->right=&all[newn];

}

}//加入紫色的节点

}

int main()

{

int m,n;int i,j,k;

for(;;)

{

if(scanf("%d%d",&m,&n)!=2) break;

for(i=0;i<m;i++)

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

{

scanf("%d",&mat[i][j]);

}

make(m,n);//由mat生成十字链表

if(dfs())

{

printf("Yes, I found it\n");

}

else

{

printf("It is impossible\n");

}

}

return 0;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: