开始楞是没看懂意思,E文让我很纠结...
要判断一条边是否为二分图中必须边,方法如下:
1、先求出原图的任意最大匹配
2、对二分图某一边的所有点,删去其当前的匹配边。删的过程不是简单的将原图设为不连通,你还得将其相应的匹配值设为未匹配。
假如原图link[a]=b; 那我们删边的时候既要讲map[b][a]设为0.,同时也要讲link[a]设为-1。(举个例子而已,数据的写法自己定)
3、对此跟新图再次从b点(承接上面的例子)进行一次最大匹配,如果此时还能完成最大匹配,那么刚才删去的那条边显然就不是必须边了。反之,必须边成立!(做完记得将图还原)
4、重复2步骤,直到所有的点都被删过了一次当前匹配边
(还有一个问题就是,再对跟新图进行最大匹配验证的过程中,这必然不可避免的会改变其他匹配边原来的信息。比如a点原来匹配着b点,在新方案中,它可能却变成了匹配c点。其实这对我们的算法没有任何影响,因为我们本来的目的就只是对点进行匹配,至于该点和那个点匹配,无所谓。最开始,我们也是任意的进行了一次二分匹配。我们删边的目的只是为了验证该点是不是还存在着其他匹配方案,至于是从哪一个方案变到哪一个方案,没有任何关系)
#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;
}