C语言二分图匹配(4)___棋盘游戏(Hdu 1281)

Description

小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。 
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么? 
C语言二分图匹配(4)___棋盘游戏(Hdu 1281)_第1张图片

Input

输入包含多组数据, 
第一行有三个数N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盘的高、宽,以及可以放“车”的格子数目。接下来的K行描述了所有格子的信息:每行两个数X和Y,表示了这个格子在棋盘中的位置。 

Output

对输入的每组数据,按照如下格式输出: 
Board T have C important blanks for L chessmen. 

Sample Input

        
        
        
        
3 3 4 1 2 1 3 2 1 2 2 3 3 4 1 2 1 3 2 1 3 2

Sample Output

        
        
        
        
Board 1 have 0 important blanks for 2 chessmen. Board 2 have 3 important blanks for 3 chessmen.
分析:显然这是一道二分图的最大匹配问题.但是这道题问并不是问最大匹配值是多少,而是问有几个点去掉了是达到最大匹配的必须点。然而我们处理这个题建图的时候是把横坐标处理成二分图的左部,纵坐标处理成二分图的右部,可放点处理成左部到右部的一条边。那么所求就是问能成为最大匹配的所有匹配中那些边是必须边。


处理方法:我们先求出最大匹配A,然后枚举每条边,去掉后求最大匹配B,如果B==A那么说明当前边无关紧要,如果B<A,那么说明当前边为必须边.

两种存边方法: 邻接表、邻接矩阵。

邻接表:


#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <queue>
#include <stdlib.h>
using namespace std;
int pre[105],visit[105];
vector<int>way[105];
int l,r;                //l,r就是被禁止的边.
int dfs(int step){
	int i,next;
	for(i=0;i<way[step].size();i++){
		next=way[step][i];
		if(visit[next] || step==l&&i==r)continue;
		visit[next]=1;
		if(pre[next]==-1 || dfs(pre[next])){
			pre[next]=step;
			return 1;
		}
	}
	return 0;
}
int main()
{
	int i,j,n,v,u,m,ans,k,rec,step,cishu=1;
	while(cin>>n>>m){
		for(i=0;i<105;i++)way[i].clear();
		cin>>k;
		for(i=0;i<k;i++){
			cin>>u>>v;
			way[u].push_back(v);
		}
		memset(pre,-1,sizeof(pre));
		ans=0;rec=0;l=99999,r=99999;
		for(i=1;i<=n;i++){
			memset(visit,0,sizeof(visit));
			if(dfs(i)==1)rec++;
		}
		for(i=1;i<=n;i++){
			for(int d=0;d<way[i].size();d++){  //枚举每条边.
			memset(pre,-1,sizeof(pre));
			step=0;
			l=i,r=d;                           //赋值给全局变量表禁止
			for(j=1;j<=n;j++){
				memset(visit,0,sizeof(visit));
				if( dfs(j)==1)step++;
			}
			if(step<rec)ans++;
		}
		}
		 printf("Board %d have %d important blanks for %d chessmen.\n",cishu,ans,rec);
		cishu++;
	} 
	return 0;
}



邻接矩阵:

#include<iostream>
const int MAXN=110;
using namespace std;
int m,n,k;
int X[MAXN],Y[MAXN];
int cx[MAXN],cy[MAXN];
int map[MAXN][MAXN];
bool mark[MAXN];

int dfs(int u){
    //考虑所以yi顶点v
    for(int v=1;v<=n;v++){
        //u与v邻接且没有被访问过
        if(!mark[v]&&map[u][v]){
            mark[v]=1;
            //如果v没有匹配,或者v已经匹配了,但从cy[v]出发可以找到一条增广路
            if(cy[v]==-1||dfs(cy[v])){
                cy[v]=u;//把u匹配给v
                cx[u]=v;//把v匹配给u
                return 1;
            }
        }
    }
    return 0;
}

//匈牙利算法
int MaxMatch(){
    int res=0;
    memset(cx,-1,sizeof(cx));//从1匹配开始增广,将cx,cy各元素初始化为-1.
    memset(cy,-1,sizeof(cy));
    for(int i=1;i<=n;i++){
        //从每个未盖点出发寻找增广路
        if(cx[i]==-1){
            memset(mark,false,sizeof(mark));
            //每寻找到一条增广路可使匹配数增加1
            if(dfs(i)){
                res++;
            }
        }
    }
    return res;
}

int main(){
    int _case=1;
    while(~scanf("%d%d%d",&n,&m,&k)){
        memset(map,0,sizeof(map));
        for(int i=1;i<=k;i++){
            scanf("%d%d",&X[i],&Y[i]);
            map[X[i]][Y[i]]=1;
        }
        int ans=MaxMatch();//求二分图最大匹配的匈牙利算法
        int count=0;
        for(int i=1;i<=k;i++){
            map[X[i]][Y[i]]=0;//试着去掉每一条边
            int res=MaxMatch();
            map[X[i]][Y[i]]=1;//恢复
            if(res<ans)count++;//如果比先前求出的小,说明是重要点
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",_case++,count,ans);
    }
    return 0;
}




你可能感兴趣的:(C语言二分图匹配(4)___棋盘游戏(Hdu 1281))