pku 1486 求出二分匹配图中的必须边

开始楞是没看懂意思,E文让我很纠结...

要判断一条边是否为二分图中必须边,方法如下:

1、先求出原图的任意最大匹配

2、对二分图某一边的所有点,删去其当前的匹配边。删的过程不是简单的将原图设为不连通,你还得将其相应的匹配值设为未匹配。

假如原图link[a]=b;  那我们删边的时候既要讲map[b][a]设为0.,同时也要讲link[a]设为-1。(举个例子而已,数据的写法自己定)

3、对此跟新图再次从b点(承接上面的例子)进行一次最大匹配,如果此时还能完成最大匹配,那么刚才删去的那条边显然就不是必须边了。反之,必须边成立!(做完记得将图还原)

4、重复2步骤,直到所有的点都被删过了一次当前匹配边

(还有一个问题就是,再对跟新图进行最大匹配验证的过程中,这必然不可避免的会改变其他匹配边原来的信息。比如a点原来匹配着b点,在新方案中,它可能却变成了匹配c点。其实这对我们的算法没有任何影响,因为我们本来的目的就只是对点进行匹配,至于该点和那个点匹配,无所谓。最开始,我们也是任意的进行了一次二分匹配。我们删边的目的只是为了验证该点是不是还存在着其他匹配方案,至于是从哪一个方案变到哪一个方案,没有任何关系)

View Code
#include<iostream>
#include
<string>
using namespace std;

int link[30];
int map[30][30];
typedef
struct node
{
int xmin;
int xmax;
int ymin;
int ymax;
}node;

node num[
30];
int n,m;
int v[30];

int find(int x)
{
for(int i=1;i<=n;i++)
{
if(!v[i] && map[x][i])
{
v[i]
=1;
if(link[i]==0 || find(link[i]))
{
link[i]
=x;
return 1;
}
}
}
return 0;
}

void solve()
{
int i;
memset(link,
0,sizeof(link));
for(i=1;i<=n;i++)
{
memset(v,
0,sizeof(v));
find(i);
}
}


int isok(int x,int y,int i)
{
if(x>=num[i].xmin && x<=num[i].xmax && y>=num[i].ymin && y<=num[i].ymax)
return 1;
return 0;
}

int main()
{
int i,j,a,b;m=1;
freopen(
"D:\\in.txt","r",stdin);
while(scanf("%d",&n),n)
{
for(i=1;i<=n;i++)
{
scanf(
"%d%d%d%d",&num[i].xmin,&num[i].xmax,&num[i].ymin,&num[i].ymax);
}
memset(map,
0,sizeof(map));
for(i=1;i<=n;i++)
{
scanf(
"%d%d",&a,&b);
for(j=1;j<=n;j++)
{
if(isok(a,b,j))
{
map[i][j]
=1; //左边代表数字,右边代表字母
}
}
}

//先任意求一次最大匹配
solve();
printf(
"Heap %d\n",m++);
int flag=0;
for(i=1;i<=n;i++)
{
if(!link[i])
continue;
int temp=link[i];
link[i]
=0; //把位置腾出来
map[temp][i]=0; //同时把边删掉,这样就无法达到原来的匹配
memset(v,0,sizeof(v));
if(!find(temp)) //如果没有新的匹配方案诞生,说明这是一条关键边
{
if(flag)
cout
<<" ";
flag
=1;
link[i]
=temp;
printf(
"(%c,%d)",(char)(i+64),temp);
}
map[temp][i]
=1; //把图复原
}
if(!flag)
{
cout
<<"none";
}
cout
<<endl<<endl;
}
return 0;
}
posted @ 2011-07-30 12:46  Accept  阅读(362)  评论(0编辑  收藏  举报