URAL - 1099
Time Limit: 500MS | Memory Limit: 65536KB | 64bit IO Format: %I64d & %I64u |
Description
Input
Output
Sample Input
input | output |
---|---|
3 1 2 2 3 1 3 | 2 1 2 |
有n个门卫 给出门卫之间可以两两一起工作的搭配 现在安排门卫工作
求出可以安排的最多的门卫数 并列举出搭配的方案
与二分匹配相比 一般图并没有二部图的关系
给出关于带花树算法的介绍 花指的就是图中的环
匹配就是一个图中一堆没有端点的边的集合,求最大匹配就是求这个边集最大有多少条边。
无论是任意图还是二分图,都有以下定理:
当前匹配是最大匹配当且仅当不存在增广路。
增广路的定义就是,一条包含奇数条边的路径,最前和最后的两条边都是非匹配边,且对于路径非两端的点,都连接着一条匹配边和非匹配边。
求图的匹配的算法就是不断地找增广路,把增广路上的匹配边变成非匹配边,非匹配边变成匹配边。
对于二分图来说,只要从一个没有被匹配到的点开始bfs(dfs)一下就能找到增广路(如果确实有增广路)。
但是对于任意图来说,从一个没有被匹配到的点开始bfs(dfs)不一定能找到,能不能找到取决于遍历的顺序。
于是,为了使任意图可以有匹配,带花树就出现。带花树其实就是一棵带着花的树。
任意图中搜索顺序对找增广路有影响主要是因为任意图中有奇数环,在bfs树上出现的这些奇数环就叫做花。
由于这些花不太和谐,所以要进行处理,于是就把这些花缩成一个点,然后继续bfs。
总的来说,带花树的算法就是按照二分图匹配中那样找增广路,遇到花就缩起来。
找到花要把花缩成一点,就要把整个花找出来,这里暴力找一下好了,暴力用的时间等于花上点的个数,由于找完后花就缩起来了,所以找一条增广路中缩点的总时间复杂度是不超过O(V)的。
枚举增广路的初始点是O(V),找一条增广路的时间复杂度是O(E)(邻接矩阵O(V^2))的,所以总的时间复杂度是O(VE)(邻接矩阵(V^3));由此看来其实带花树的时间复杂度和匈牙利算法的时间复杂度是一样的(显然带花树的常数要大很多)。
以上介绍来自: 点击打开链接
这题比较简单 就是根据模板 求出相应的匹配就可以了
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>
#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 10009
#define MAXN 250
#define INF 99999999
#define ll __int64
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
using namespace std;
int Read()
{
char ch;
int a = 0;
while((ch = getchar()) == ' ' | ch == '\n');
a += ch - '0';
while((ch = getchar()) != ' ' && ch != '\n')
{
a *= 10;
a += ch - '0';
}
return a;
}
void Print(int a)
{
if(a>9)
Print(a/10);
putchar(a%10+'0');
}
deque<int> Q;
//g[i][j]存放关系图:i,j是否有边 match[i]存放i所匹配的点
bool g[MAXN][MAXN],inque[MAXN],inblossom[MAXN],inpath[MAXN];
int match[MAXN],pre[MAXN],base[MAXN];
int n,m,mmg;
//vector<int> res;
struct node
{
int u,v;
}point[MAXN];
//找公共祖先
int findancestor(int u,int v)
{
MEM(inpath,0);
while(1)
{
u=base[u];
inpath[u]=1;
if(match[u]==-1) break;
u=pre[match[u]];
}
while(1)
{
v=base[v];
if(inpath[v]) return v;
v=pre[match[v]];
}
}
//压缩花
void reset(int u,int anc)
{
while(u!=anc)
{
int v=match[u];
inblossom[base[u]]=1;
inblossom[base[v]]=1;
v=pre[v];
if(base[v]!=anc) pre[v]=match[u];
u=v;
}
}
void contract(int u,int v,int n)
{
int anc=findancestor(u,v);
MEM(inblossom,0);
reset(u,anc); reset(v,anc);
if(base[u]!=anc) pre[u]=v;
if(base[v]!=anc) pre[v]=u;
for(int i=1;i<=n;i++)
{
if(inblossom[base[i]])
{
base[i]=anc;
if(!inque[i])
{
Q.push_back(i);
inque[i]=1;
}
}
}
}
bool dfs(int S,int n)
{
for(int i=0;i<=n;i++)
{
pre[i]=-1; inque[i]=0; base[i]=i;
}
Q.clear(); Q.push_back(S); inque[S]=1;
while(!Q.empty())
{
int u=Q.front(); Q.pop_front();
for(int v=1;v<=n;v++)
{
if(g[u][v]&&base[v]!=base[u]&&match[u]!=v)
{
if(v==S||(match[v]!=-1&&pre[match[v]]!=-1)) contract(u,v,n);
else if(pre[v]==-1)
{
pre[v]=u;
if(match[v]!=-1)
{
Q.push_back(match[v]);
inque[match[v]]=1;
}
else
{
u=v;
while(u!=-1)
{
v=pre[u];
int w=match[v];
match[u]=v;
match[v]=u;
u=w;
}
return 1;
}
}
}
}
}
return 0;
}
int solve()
{
int ans=0;
MEM(match,-1);
for(int i=1;i<=n;i++)
if(match[i]==-1&&dfs(i,n)) ans++;
return ans;
}
int main()
{
// fread;
scanf("%d",&n);
int u,v;
MEM(g,0);
while(scanf("%d%d",&u,&v)!=EOF)
{
g[u][v]=g[v][u]=1;
}
int res=solve();
printf("%d\n",res*2);
int flag=0;
for(int i=1;i<=n;i++)
{
if(match[i]!=-1)
{
printf("%d %d\n",i,match[i]);
match[i]=match[match[i]]=-1;
}
}
}