• UOJ80 二分图最大权匹配


    草,学了一下午假板子,sb博客害人

    题目大意:

    一个教室有(n)个男生和(m)个女生,某些男女之间愿意早恋(雾),其早恋好感度为(w_i),问怎样让男女配对使得班里好感度之和最大

    (nle 400)

    二分图最优匹配模型,KM算法板子题

    二分图中我们介绍了一些定义,包括最优匹配、完美匹配、顶标、相等子图

    相等子图的完美匹配就是二分图的最优匹配

    我们考虑先给每个点赋顶标,或者说对于好感度的期望值

    首先每个男生的期望是她愿意早恋对象的好感度的最大值,女生是(0)(即每个男生都想要最喜欢的人,女生都很佛)

    这样必定满足(a_u+b_vge val(u,v))

    然后试图在这个的相等子图中寻找完美匹配

    不过大概率找不到

    找不到怎么办呢?一般是出现了两个男生想要一个女生的情况,那么我们只能给某一个男生安排另外的女生,所以我们要降低已经匹配过男生的期望

    当然为了不漏掉任何一个女生,我们不能降低的太多,应该降低到刚好刚才没被人选过的女生可以入选

    也就是说我们对于每个刚才挑过人的男生,应该降低(d=min{a_u+b_v-val(u,v)})其中(vis_{boy}[u]=1,vis_{girl}[v]=0)

    但是男生们期望降低了,刚才匹配过的女生不能不要啊(qwq),所以应该将刚才每个匹配过的女生期望增加(d)保证刚才被匹配过的女生不会被丢出相等子图

    这样会保证参与过匹配的边不会被踢出匹配,同时保证有新的右部节点参与匹配

    但是(n e m)的情况怎么办啊(雾)

    那为了匹配顺利,我们只能添加好感度为(0)的假人了……

    具体实现一些细节在代码里面:

    (dfs)版本:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    namespace red{
    #define int long long
    #define eps (1e-8)
    	inline int read()
    	{
    		int x=0;char ch,f=1;
    		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    		if(ch=='-') f=0,ch=getchar();
    		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    		return f?x:-x;
    	}
    	const int N=410,inf=0x3f3f3f3f;
    	int n,m,k;
    	int bl,br;
    	int head[N],cnt;
    	int love[N][N];
    	int g[N];
    	int f[N],ex_girl[N],ex_boy[N];
    	int slack[N];
    	bool vis_boy[N],vis_girl[N];
    	inline bool find(int boy)//稍微魔改过的匈牙利
    	{
    		vis_boy[boy]=1;
    		for(int girl=1;girl<=m;++girl)
    		{
    			if(vis_girl[girl]) continue;
    			int gap=ex_boy[boy]+ex_girl[girl]-love[boy][girl];
    			if(gap==0)//如果是相等子图,直接更新
    			{
    				vis_girl[girl]=1;
    				if(!f[girl]||find(f[girl]))
    				{
    					f[girl]=boy;
    					g[boy]=girl;
    					return 1;
    				}
    			}
    			else//不是的话记录一下最少改变多少期望
    			{
    				slack[girl]=min(slack[girl],gap);
    			}
    		}
    		return 0;
    	}
    	inline void km()
    	{
    		for(int i=1;i<=n;++i)
    		{
    			for(int j=1;j<=n;++j)
    			{
    				ex_boy[i]=max(ex_boy[i],love[i][j]);//男生初始期望是所有好感度最大值
    			}
    		}
    		for(int i=1;i<=n;++i)
    		{
    			memset(slack,inf,sizeof(slack));
    			memset(vis_boy,0,sizeof(vis_boy));
    			memset(vis_girl,0,sizeof(vis_girl));
    			if(find(i)) continue;//如果直接匹配成功就可以跳了
    			while("haku")//失败了,扩大相等子图范围
    			{
    				int d=inf,t;
    				for(int j=1;j<=n;++j)
    					if(!vis_girl[j]) d=min(d,slack[j]);//找到女生没被匹配过里面需要降低的最小值
    				for(int j=1;j<=n;++j)
    				{
    					if(vis_boy[j]) ex_boy[j]-=d;//匹配过的男生降低d
    					if(vis_girl[j]) ex_girl[j]+=d;//匹配过的女生提高d
    					else//没被匹配过的女生
    					{
    						slack[j]-=d;
    						if(!slack[j]) t=j;//如果男生全部削减完d之后可以进入相等子图,做个标记
    					}
    				}
    				if(!f[t]) break;//如果进入的女生没有配对的男生,那么说明我们可以找到新的一对,变成完美匹配
    				vis_girl[t]=1,vis_boy[f[t]]=1;//否则继续改变期望
    				t=f[t];
    				for(int j=1;j<=n;++j)
    					slack[j]=min(slack[j],ex_boy[t]+ex_girl[j]-love[t][j]);
    			}//注意不要在里面多次匈牙利,复杂度会炸
    			memset(vis_boy,0,sizeof(vis_boy));
    			memset(vis_girl,0,sizeof(vis_girl));
    			find(i);
    		}
    		int ret=0;
    		for(int i=1;i<=bl;++i)
    		{
    			ret+=love[i][g[i]];
    		}
    		printf("%lld
    ",ret);
    		for(int i=1;i<=bl;++i) printf("%lld ",love[i][g[i]]?g[i]:0ll);
    		puts("");
    	}
    	inline void main()
    	{
    		n=read(),m=read(),k=read();
    		bl=n,br=m;
    		n=max(n,m),m=n;//假人代打
    		for(int x,y,w,i=1;i<=k;++i)
    		{
    			x=read(),y=read(),w=read();
    			love[x][y]=w;
    		}
    		km();
    	}
    }
    signed main()
    {
    	red::main();
    return 0;
    }
    

    (bfs)版本鸽子了,以后有空再补吧

  • 相关阅读:
    java Thread和Runnable区别
    java sleep() 、yield()
    Java Thread.join()方法
    内存管理_深入剖析volatile关键字
    内存管理_JAVA内存管理
    内存管理_原子性、可见性、有序性
    小程序wx.showToast()方法实现文字换行
    常用表单校验(手机号、固话、身份证、真是姓名、邮箱、银行卡)
    通过CSS实现 文字渐变色 的两种方式
    substring和substr的区别
  • 原文地址:https://www.cnblogs.com/knife-rose/p/12093110.html
Copyright © 2020-2023  润新知