1. 程式人生 > 實用技巧 >「刷題筆記」二分圖匹配

「刷題筆記」二分圖匹配

板子

兩種寫法:(核心思想是一樣的,使用時實測並沒有什麼區別

兩個引數版(洛谷題解

bool dfs(ll u,ll ti)
{
	if(vis[u]==ti)return 0;
	vis[u]=ti;
	for(int i=head[u];i;i=next[i])
	{
		if(mh[e[i].v]==0||dfs(mh[e[i].v],ti))
		{
			mh[e[i].v]=u;
			return 1;
		}
	}
	return 0;
}

一個引數版(網上題解多用

bool find(int u)
{
	for(int i=head[u];i;i=next[i])
	{
		int v=e[i].v;
		if(!vis[v])
		{
			vis[v]=1;
			if(mh[v]==0||find(mh[v]))
			{
				mh[v]=u;
				return 1;
			}
		}
	}
	return 0;
}

特別注意:使用一個引數版時,每次使用前vis陣列都要清零!

超級英雄Hero

啊這,這不是板子嗎?
然後就因為不讀題+2了。
建圖:錦囊妙計編號是一邊,題目編號是一邊,從錦囊向題目連邊,然後跑匈牙利
注意題意:只有通過一題才能繼續答題,所以如果中途斷了直接跳出
另外:二分圖有重邊似乎對匈牙利演算法並沒有影響

文理分班

首先建圖,考慮到分班前後座位的變化,我們可以用前後座位建圖,如果之前坐在\(a\)處的同學分班後有可能坐到\(b\)處,則從\(a\)\(b\)連邊。
同時注意,如果一個人是本班的,且不調班,那他是可以不換座位的,這樣我們就直接從他的座位\(a\)\(a\)連邊
那麼只要最後每個人都有座位坐就是可行的方案
所以建圖完了以後我們跑二分圖匹配,如果全都能夠匹配則可行

放置機器人

建圖過程很有意思的一道題~
可以分別考慮每一行,每一列,並將每行每列以牆分隔成幾個小部分,那麼顯然,每一個小部分中是隻能放置一個機器人的。
我們又知道給定的行列資訊能確定唯一位置,分隔後同樣如此。我們可以把所有空地 按行分割所屬部分 的編號向它們 按列分割所屬部分 的編號連邊,那麼每一條邊都代表了一個可放置機器人的位置,在得到的這個二分圖上跑匈牙利,那麼最大匹配邊數即為最大能放置的機器人數。
挺有意思的,放下程式碼

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define ZZ_zuozhe int main()
#define N 55
#define E 2550

struct edge
{
	ll u,v;
}e[E];
ll tot,head[E],next[E];
void add(ll a,ll b)
{
	++tot;
	e[tot].u=a;
	e[tot].v=b;
	next[tot]=head[a];
	head[a]=tot;
}

ll T;
ll n,m;
ll tmp,xcn,ycn,xt;
ll kd[N][N],xnm[N][N],ynm[N][N];

ll in(ll &a)
{
	char ch=getchar();
	if(ch=='#')return a=0;
	else if(ch=='o')return a=1;
	else if(ch=='*')return a=2;
}

ll vis[E],mh[E];

bool find(ll u)
{
	for(int i=head[u];i;i=next[i])
	{
		ll v=e[i].v;
		if(!vis[v])
		{
			vis[v]=1;
			if(mh[v]==0||find(mh[v]))
			{
				mh[v]=u;
				return 1;
			}
		}
	}
	return 0;
}

ll ans;

ZZ_zuozhe
{
	memset(e,0,sizeof e);
	memset(kd,0,sizeof kd);
	memset(xnm,0,sizeof xnm);
	memset(ynm,0,sizeof ynm);
	memset(mh,0,sizeof mh);
	tmp=xt=ans=0;
	xcn=ycn=1;
	scanf("%lld%lld",&n,&m);
	getchar();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			in(kd[i][j]);
	//		cout<<kd[i][j]<<' ';
			if(j==1&&tmp==1)tmp=0,xcn++;
			if(kd[i][j]==1)xnm[i][j]=xcn,tmp=1,xt=xcn;
			if(kd[i][j]==0&&tmp==1)tmp=0,xcn++;
		}
		getchar();
	}
	tmp=0;
	for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)
	{
		if(j==1&&tmp==1)tmp=0,ycn++;
		if(kd[j][i]==1)ynm[j][i]=ycn,tmp=1;
		if(kd[j][i]==0&&tmp==1)tmp=0,ycn++;
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
	{
		add(xnm[i][j],ynm[i][j]);
	}
	for(int i=1;i<=xt;i++)
	{
		memset(vis,0,sizeof vis);
		if(find(i))ans++;
	}
//	cout<<xcn<<' '<<ycn<<' '<<xt<<endl;
	printf("%lld\n",ans);
	return 0;
}

貓和狗

運用了最大獨立集的思想
關於最大獨立集什麼的:這篇部落格寫的超超超超詳細啊qaq
明確了定義,我們來看題。