Toj 3345/hdu 1281 Chinese Chess 二分图匹配


题意:棋盘上有很多点,问最少设置几个棋子可以把这些点都吃掉,还问哪些棋子放置的位置是固定的,不然有些点就不能被吃掉

关键匹配:

先找到最大匹配。

然后对每个匹配边的两个端点u,v查看是否对应唯一的匹配边,

不优化会超时的。看了别人的代码才发现可以这样优化。太神奇了

其中有两处优化


#include "stdio.h"
#include "string.h"
#include "queue"
#include "vector"
#include "algorithm"
using namespace std;
const int maxn = 10005;
const int inf = 1<<30;
int n,m,k,st,ed;
vector<int>map[maxn],map1[maxn];
int cx[maxn],cy[maxn],Cx[maxn];
bool vis[maxn];
bool FindPath( int u )
{
	for( int i = 0; i < map[u].size(); i ++ )
	{
		int v = map[u][i];
		if( vis[v] )	continue;
		if( st == u && ed == v )	continue;
		vis[v] = 1;
		if( cy[v] == -1 || FindPath( cy[v] ) )
		{
			cx[u] = v;
			cy[v] = u;
			return true;
		}
	}
	return false;
}
bool FindPathY( int v )
{
	for( int i = 0; i < map1[v].size(); i ++ )
	{
		int u = map1[v][i];
		if( vis[u] )	continue;
		if( st == u && ed == v )	continue;
		vis[u] = 1;
		if( cx[u] == -1 || FindPathY( cx[u] ) )
		{
			cx[u] = v;
			cy[v] = u;
			return true;
		}
	}
	return false;
}
int MaxMatch()  //二分图匹配
{
	int ans = 0;
	memset( cx,-1,sizeof(cx) );
	memset( cy,-1,sizeof(cy) );
	for( int i = 1; i <= n; i ++ )         //优化,枚举每条边,查看是否可以找到匹配边
	{
		if( cx[i] == -1 )
		{
			for( int j = 0; j < map[i].size() && cx[i] == -1; j ++ )
			{
				int v = map[i][j];
				if( cy[v] == -1 )
				{
					cy[v] = i;
					cx[i] = v;
					ans ++;
				}
			}
		}
	}
	for( int i = 1; i <= n; i ++ )   //寻找增广路,增加匹配边 
	{
		if( cx[i] == -1 )
		{
			memset( vis,0,sizeof(vis) );
			ans += FindPath( i );
		}
	}
	return ans;
}
int fun()
{
	int ans = 0,flag;
	for( int i = 1; i <= n; i ++ )	Cx[i] = cx[i];
	for( int i = 1; i <= n; i ++ )
	{
		if( Cx[i] != -1 )
		{
			flag = 0;            //查看一条边的两个端点是否都没有增广路 等0即为关键点
			st = i; ed = cx[i];
			cx[st] = cy[ed] = -1;
			memset( vis,0,sizeof(vis) );
			if( FindPath( st ) )	flag = 1;      //判断左端点能否匹配其他点
			else
			{
				memset( vis,0,sizeof(vis) );
				if( FindPathY( ed ) )	flag = 1;      //判断右端点能否匹配其他点
			}
			if( !flag )
			{
				cx[st] = ed;
				cy[ed] = st;
				ans ++;
			}
			else
				for( int j = i; j <= n; j ++ )
					if( cx[j] != Cx[j] )
						Cx[j] = -1;
			//如果在寻找增广路时,匹配边上的点发生变化,说明表示关键匹配,  
			//下次无需再进行判断了  
		}
	}
	return ans;

}
int main()
{
#ifndef ONLINE_JUDGE   
	freopen("data.txt","r",stdin);   
#endif
	int x,y,cas = 1;
	while( scanf("%d%d%d",&n,&m,&k)!=EOF )
	{
		for( int i = 1; i <= n; i ++ ) map[i].clear();
		for( int i = 1; i <= m; i ++ ) map1[i].clear();
		for( int i = 0; i < k; i ++ )
		{
			scanf("%d%d",&x,&y);
			map[x].push_back( y );
			map1[y].push_back( x );
		}
		st = ed = -1;
		int cur = MaxMatch();
		int cnt = fun();
		printf("Board %d have %d important blanks for %d chessmen.\n",cas ++,cnt,cur);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值