zoukankan      html  css  js  c++  java
  • KM bfs写法

    KM bfs写法

    2018astar资格赛的第三题整数规划

    (x, y)看成二分图两边的顶标,(a_{ij})就是二分图的边权,整道题其实就是求二分图的最大权匹配。
    然后打了个(dfs)(KM)(TLE)了,后来听别人说要用(bfs)的写法,因为那个才是真正的(O(n^3))(dfs)的写法最坏情况还是(O(n^4))

    原理是一样的,只不过(bfs)有一点点像迭代,每一次也只是搜(diff=0)的情况,而且右边的点只会搜索一次(或者说是左边的点只会搜索一次,即左边的每个点只会进队一次),用(pre)记住当前的交错路径,找到未匹配的就可以沿交错路径进行修改。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    const int maxn=210;
    const LL inf=1LL<<60;
    
    int n;
    
    namespace KM
    {
    	int n;
    	LL mat[maxn][maxn];               //边权
    	int matcha[maxn], matchb[maxn];   //左边的点匹配的右边点;右边的点匹配的左边点
    	LL marka[maxn], markb[maxn];      //左顶标;右顶标
    	LL slack[maxn];                   //松弛数组
    	bool visa[maxn], visb[maxn];      //访问标记
    
    	int head, tail;
    	int q[maxn], pre[maxn];           //队列;交错路径
    
    	bool check(int cur)
    	{
    		visb[cur]=true;     //标记cur已搜索
    		if (matchb[cur])    //已匹配,即当前匹配失败
    		{
    			if (!visa[matchb[cur]])    //匹配的点是否已进队
    			{
    				q[++tail]=matchb[cur];
    				visa[matchb[cur]]=true;
    			}
    			return false;
    		}
    		//未匹配,即当前匹配成功,沿交错路径进行匹配
    		while (cur)
    			swap(cur, matcha[matchb[cur]=pre[cur]]);
    		return true;
    	}
    
    	void bfs(int start)
    	{
    		fill(visa, visa+1+n, false);
    		fill(visb, visb+1+n, false);
    		fill(slack, slack+1+n, inf);
    
    		head=tail=1;
    		q[1]=start;
    		visa[start]=true;
    
    		while (1)
    		{
    			while (head<=tail)
    			{
    				int cur=q[head++];
    				for (int i=1; i<=n; ++i)
    				{
    					LL diff=marka[cur]+markb[i]-mat[cur][i];
    					if (!visb[i] && diff<=slack[i])   //visb=true说明已搜索,无需更新slack和pre,也是保证pre的正确性
    					{
    						slack[i]=diff;
    						pre[i]=cur;
    						if (diff==0)  //diff=0,可以尝试匹配
    							if (check(i)) return; //匹配成功可直接返回
    					}
    				}
    			}
    
    			LL delta=inf;
    			for (int i=1; i<=n; ++i)
    				if (!visb[i] && slack[i]) delta=min(slack[i], delta);
    			for (int i=1; i<=n; ++i)    //松弛
    			{
    				if (visa[i]) marka[i]-=delta;
    				if (visb[i]) markb[i]+=delta;
    				else slack[i]-=delta;   //维护slack的正确性(参考diff的计算及marka,markb的变化)
    			}
    
    			head=1, tail=0;
    			for (int i=1; i<=n; ++i)
    				if (!visb[i] && !slack[i] && check(i)) return;
    				//松弛后尝试匹配diff=0的点。
    		}
    	}
    
    	void solve()
    	{
    		fill(matcha, matcha+1+n, 0);
    		fill(matchb, matchb+1+n, 0);
    		fill(markb, markb+1+n, 0);
    
    		for (int i=1; i<=n; ++i)
    		{
    			marka[i]=0;
    			for (int j=1; j<=n; ++j)
    				marka[i]=max(marka[i], mat[i][j]);
    		}
    
    		for (int i=1; i<=n; ++i) bfs(i);
    	}
    }
    
    void read()
    {
    	scanf("%d", &n);
    	KM::n=n;
    	for (int i=1; i<=n; ++i)
    		for (int j=1; j<=n; ++j)
    		{
    			int x;
    			scanf("%d", &x);
    			KM::mat[i][j]=-x;
    		}
    }
    
    void solve()
    {
    	KM::solve();
    	LL ans=0;
    	for (int i=1; i<=n; ++i)
    		ans+=KM::marka[i]+KM::markb[i];
    	printf("%lld
    ", -ans);
    }
    
    int main()
    {
    	int casesum;
    	scanf("%d", &casesum);
    	for (int i=1; i<=casesum; ++i)
    	{
    		printf("Case #%d: ", i);
    		read();
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    javascritp对fckeditor编辑器赋值取值
    [.net]发布基于C#与Flash通信的游戏设计器完整项目及源代码下载
    Sun VirtualBox更新到3.0.8.53138
    JavaScript强弹代码
    给Repeater、Datalist和Datagrid增加自动编号列
    Adobe发布新版Photoshop.com 支持iPhone
    网站免费提交搜索引擎入口地址?
    利用FMS做在线视频录制
    Apache Lucene 2.9的改进
    KDE 4.3.2 发布
  • 原文地址:https://www.cnblogs.com/GerynOhenz/p/9458006.html
Copyright © 2011-2022 走看看