CH 白色情人节1(②第一天-KM算法)

背景
下过雨的夏天傍晚 我都会期待
唱歌的蝉 嘿 把星星都吵醒 月光晒了很凉快
就是这样回忆起来 第一次告白
尴尬的我 看 爱装得很哲学的你其实很可爱
你说活在明天 活在期待 不如活得今天很自在
我说我懂了 会不会太快 未来 第一天要展开
描述
时间回溯到男女主认识的第一天——女主同学举办的联谊会,男主迟到了那天的联谊会,而男主到达现场时女主也刚好没有男伴,这就是他们的第一次相遇。
男主回想起来不禁一阵感慨(为啥这么好的妹子你们都不搭讪呢),突然男主想到,如果当时 已经配对的n对男女好感值之和不是 最大,会不会自己就遇不上女主了呢?
输入格式
第一行两个正整数n和queen,表示已有n对男女可以配对和女主的编号queen。(男编号为X 1~X n,女编号为Y 1~Y n+1)
接下来n*(n+1)行,每行三个正整数X i,Y i,Z i,表示男X i女Y i之间的好感度为Z i保证每对(Xi,Yi)没有重复,即两人的关系至多一条
不存在男与男、女与女之间的好感度!
输出格式
前n行输出联谊会上n对男女的配对情况 (会多出来一名女生),每行两个正整数X i和Y i(空格隔开),任意一种符合条件的方案均可。
第n+1行, 对于你输出的方案,如果女主未配对,输出"YES"(不含引号),表示女主会遇到男主,否则输出"NO"(不含引号)。
样例输入
2 3
1 1 15
1 2 20
1 3 17
2 1 22
2 2 14
2 3 14

样例输出
1 2
2 1
YES

数据范围与约定
对于100%的数据, 1\leq n\leq 520,1\leq Z_{i}\leq 201314
样例解释
如图,已经配对的关系有是 男1号女2号男2号女1号
来源
*****关系~


km算法

就是二分图匹配时维护lx[i],ly[i]

满足

1.对于任意边lx[i]+ly[i]>=w[i,j]

2.如果存在一组匹配,其中任意边满足lx[i]+ly[i]=w[i,j],它一定是最大匹配(如果随意调换一边,必然出现lx[i]+ly[j]+lx[i']+ly[j']=w[i,j]+w[i',j']≥w[i,j']+w[i',j]

于是每次找出slock,表示对所有找过的i,不为0的min(lx[i]+ly[j]-w[i,j])

把slock的值从左移至右,既不改变原图能走的边,又能使图得到扩充。

//一开始应该把lx[i]设成max(w[i,j],0),ly[i]设成0

每次扩充必须保证图中的边满足lx[i]+ly[i]=w[i,j],否则用slock扩充


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
using namespace std;
#define INF (100000000)
#define MAXN (520+10)
#define MAXWi (201314)
int n,m,yi,slack;
int w[MAXN][MAXN],lx[MAXN],ly[MAXN],a[MAXN];//a=?->yi
bool vx[MAXN],vy[MAXN];
bool find(int x)
{
	vx[x]=1;
	for (int i=1;i<=m;i++)
		if (!vy[i])
		{
			int t=lx[x]+ly[i]-w[x][i];
			if (t==0)
			{
				vy[i]=1;
				if (a[i]==0||find(a[i])) {a[i]=x;return 1;}			
			}else slack=min(slack,t);		
		}
	return 0;
}
int main()
{
//	freopen("input","r",stdin);
	scanf("%d%d",&n,&yi);m=n+1;
	memset(lx,0,sizeof(lx));
	memset(ly,0,sizeof(ly));
	memset(a,0,sizeof(a));
	for (int i=1;i<=n*(n+1);i++)
	{
		int u,v,wei;scanf("%d%d%d",&u,&v,&wei);w[u][v]=wei;
		lx[u]=max(lx[u],wei);
	}
	for (int i=1;i<=n;i++)
	{
		memset(vx,0,sizeof vx);
		memset(vy,0,sizeof vy);
		slack=INF;
		while (!find(i))
		{
			for (int j=1;j<=n;j++) if (vx[j]) lx[j]-=slack;
			for (int j=1;j<=m;j++) if (vy[j]) ly[j]+=slack;
			memset(vx,0,sizeof vx);
			memset(vy,0,sizeof vy);
			slack=INF;
		//	find(i);		
		}
	}	
	for (int i=1;i<=m;i++)
	{
		if (a[i]) printf("%d %d\n",a[i],i);
	}
	if (a[yi]) printf("NO\n");
	else printf("YES\n");
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值