二分圖多重匹配
一般的二分圖匹配與多重匹配之間的區別就是:
二分圖多重匹配:右邊的物品可以和多個左邊的相匹配,同時右邊的物品可以有一個最大匹配容量V[i]
int cnt[maxn],V[maxn];//cnt[i]記錄現在第i個星球有多少個人,V[i]記錄i星球的容量
int linker[maxn][15];//linker[i][j]表示第i個星球第j個匹配的人是誰
bool dfs(int u)
{
for(int i=1;i<=m;i++)
{
if(mapp[u][i]&&!vis[i])//表示第u個人可以匹配第i個星球
{
vis[i]=1;
if(cnt[i]<V[i])//並且該星球人數未上限,直接匹配
{
linker[i][cnt[i]++]=u;
return true;
}
for(int j=0;j<V[i];j++)
{
if(dfs(linker[i][j]))//可以讓位置
{
linker[i][j]=u;
return true;
}
}
}
}
return false;
}
bool solve()//判斷左是否都有匹配一個右,即最大匹配數爲n
{
for(int i=1;i<=n;i++)//查看是否所以人可以逃離
{
mem(vis,false);
if(!dfs(i))return false;//該人不可以逃離就輸出NO
}
return true;
}
int count_()//輸出多重最大匹配數
{
mem(linker,-1);
mem(cnt,0);
int res=0;
for (int i=1;i<=n;i++)
{
mem(vis,false);
if (dfs(i))res++;
}
return res;
}
例題1:HDU3605
https://vjudge.net/problem/HDU-3605
直接給出了左右對應關係,並且給出了右邊的容量大小,直接套板子即可,題中所問的是能不能讓所有人逃出,所以沒必要算最大匹配數(如果算也是可以的
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e5+7;
const int INF=1e9;
const ll INFF=1e18;
int mapp[maxn][15];
bool vis[maxn];
int linker[maxn][15];
int cnt[maxn],V[maxn],n,m,x;
bool dfs(int u)
{
for (int i=0;i<m;i++)
{
if (mapp[u][i]&&!vis[i])
{
vis[i]=true;
if (cnt[i]<V[i])
{
linker[i][cnt[i]++]=u;
return true;
}
for (int j=0;j<V[i];j++)
{
if (dfs(linker[i][j]))
{
linker[i][j]=u;
return true;
}
}
}
}
return false;
}
bool solve()
{
rep(i,1,n)
{
repp(j,0,m)vis[j]=false;
if (!dfs(i))return false;
}
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if (!n&&!m)break;
repp(i,0,m)cnt[i]=0;
rep(i,1,n)
{
repp(j,0,m)
{
scanf("%d",&x);
if (x==1)mapp[i][j]=1;
else mapp[i][j]=0;
}
}
repp(i,0,m)scanf("%d",&V[i]);
if (solve())cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
例題2:POJ2289
https://vjudge.net/problem/POJ-2289
左邊與右邊多重匹配,問右邊的最大匹配數的最小值,即假設所有的都不超過一個值limit,這個limit就是最終的答案,二分答案limit,找到最小的limit
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e3+7;
const int INF=1e9;
const ll INFF=1e18;
int mapp[maxn][maxn],cnt[maxn];
int linker[maxn][maxn];
bool vis[maxn];
int n,m,limit,x;
char s[maxn],ch;
void init()
{
mem(mapp,0);
}
bool dfs(int u)
{
for (int i=0;i<m;i++)
{
if (mapp[u][i]&&!vis[i])
{
vis[i]=true;
if (cnt[i]<limit)
{
linker[i][cnt[i]++]=u;
return true;
}
for (int j=0;j<cnt[i];j++)
{
if (dfs(linker[i][j]))
{
linker[i][j]=u;
return true;
}
}
}
}
return false;
}
bool count_()
{
mem(cnt,0);
for (int u=1;u<=n;u++)
{
mem(vis,false);
if (!dfs(u))return false;
}
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if (!n&&!m)break;
init();
rep(i,1,n)
{
scanf("%s",s);
while(1)
{
scanf("%d%c",&x,&ch);
mapp[i][x]=1;
if (ch=='\n')break;
}
}
int l=0,r=n;
while(l<r)
{
limit=(l+r)/2;
if (count_())r=limit;
else l=limit+1;
}
W(r);
}
return 0;
}