二分图匹配 匈牙利算法

话不多说,只是贴板子

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#define LL long long
using namespace std;

inline void du(int &d) 
{ 
	char t=getchar();  bool Mark=false; 
	for(;t<'0'||t>'9';t=getchar())if(t=='-')Mark=!Mark; 
	for(d=0;t>='0'&&t<='9';t=getchar())d=d*10+t-'0'; 
	if(Mark)d=-d; 
}

//  本页代码 POJ 1274 
//  二分图匹配模板

const  int  maxn = 600;
const  int  maxe = 100000;
int  edge_cnt = 0;   // 链式前向星 的依赖
int  Last[maxe], Next[maxe], Des[maxe], Len[maxe];  // 链式前向星存图, len非必要
int  link[maxn];	//  右边的点与左边匹配点的编号
int visited[maxn];   // 最新访问的轮数/时间戳, 用于判断在某一次增广是是否经过了该点(右边的点)
int  times;   //时间戳
int  n, m;     //  n 为左边节点数, m为右边节点数


void addedge(int x, int y, int value);
bool Find(int u);
int  Hungarian()
{
	for(int i = 1; i <= m; i ++)link[i] = -1;   //  -1 表示还没有匹配上
	memset(visited,0,sizeof(visited));   // 清空上一组数据的时间戳(这个不清空也没关系)
	times = 0;	// 时间戳归零 (如果上面不清空,那么这个也就不归零)

	int ans = 0;
	for(int i = 1; i <= n; i ++){ times++;  if(Find(i))ans++; }	// 依次对左边的每一个点开始进行增广, 成功就ans++
	return ans;
}

void solve()
{
	int x, y ,s , i , j ;

	//  对于每组数据, 清空前面数据的残留
	memset(Last, 0, sizeof(Last) );
	memset(Next, 0 , sizeof(Next) );
	memset(Des, 0 , sizeof(Des) );
	edge_cnt = 0;

	for(i = 1; i <= n; i++)
	{
		du(s); 
		for(j = 1; j <= s; j++)
		{
			du(x);
			addedge(i, x, 1);	//  建边, 左边第i 个点 与 右边第x个点相连
		}
	}

	int ans = Hungarian();
	cout << ans << endl;
}

int main()
{
	while(scanf("%d%d",&n, &m) != EOF )solve();
}

bool Find(int u)
{
	for( int i = Last[u], v; i != 0; i = Next[i] )
	{
		v = Des[i];
		if(visited[v] == times)continue;	// times是当前是第几次尝试增广,时间戳
		visited[v] = times;	//  更新时间戳
		if(link[v] == -1 || Find(link[v]) )  //  v是尚未匹配的点, 或者 link[v] 能成功增广
		{
			link[v] = u;
			return true;
		}
	}
	return false;
}

void addedge(int x, int y, int value)
{
	edge_cnt++;
	Next[edge_cnt] = Last[x];      Last[x] = edge_cnt;
	Des[edge_cnt] = y;  			Len[edge_cnt] = value;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值