您的位置:首页 > 产品设计 > UI/UE

poj 3715 Blue and Red(二分图最大匹配匈牙利算法)

2012-03-01 21:23 766 查看
【题目大意】:给出n个士兵和m组关系,每个士兵均被划分在Blue组或者是Red组,m组关系描述的是士兵x,y直接是否是亲密关系。由于具备亲密关系的两个士兵被分在两个不同的组,那么对训练结果会产生不好的影响,问你最少去掉多少个士兵能够避免掉这种影响。并输出字典序最小的方案。

【解题思路】:题目一看下来,第一感觉就是写个二分图的最大匹配,所以一开始加了个源点和汇点就敲了个网络流。最大匹配是搞定了~问题是方案不会搞,所以yy了一下~想想自己没写过匈牙利算法就试一下吧,顺便学学。匈牙利算法是利用dfs寻找增广路径来确定最大匹配的。因为题目要求的是字典序最小,因此我们不妨从0~n-1进行枚举,删除该点后对新的图跑一次匈牙利算法,得到的新的增广路,观察新的增广路径是否变小,若变小则说明此点必在答案中。

【匈牙利算法】:摘自网络:

匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

假设:M是G的一个匹配。

  M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。

  M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。
  M-可增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。(不要和流网络中的增广路径弄混了)

增广路的定义(也称增广轨或交错轨):
  若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。
  由增广路的定义可以推出下述三个结论:
  1-P的路径个数必定为奇数,第一条边和最后一条边都不属于M。
  2-将M和P进行取反操作可以得到一个更大的匹配M’。
  3-M为G的最大匹配当且仅当不存在M的增广路径。

时间复杂度 邻接矩阵:最坏为O(n^3) 邻接表:O(mn)  空间复杂度 邻接矩阵:O(n^2) 邻接表:O(m+n)

【代码】:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

#define maxn 300
#define maxm 21000

struct edge{
int u,v;
int next;
};
int eh[maxn],tot,ans;
int T,n,m;
edge et[maxm];
bool visit[maxn];
int match[maxn];
int po[maxn];

void addd(int u,int v){
edge E = {u,v,eh[u]};
et[tot] = E, eh[u] = tot++;
}
void addedge(int u,int v){
addd(u,v);
}

bool dfs(int u) {
for (int i = eh[u]; i != -1; i = et[i].next){
if (!visit[et[i].v] && po[et[i].v]==1){
visit[et[i].v]=true;
if (match[et[i].v] == -1 || dfs(match[et[i].v])) {
match[et[i].v] = u;
return true;
}
}
}
return false;
}
int Match() {
int cnt = 0;
memset(match, -1, sizeof (match));
for (int u = 0; u < n; u++) {
memset(visit, false, sizeof (visit));
if (!po[u] && dfs(u)) cnt++;
}
return cnt;
}

void solve(){
int anss[maxn];
int cnt=0;
for(int i=0; i<n; i++){
po[i]=po[i]^1;
int tmp=Match();
if(tmp<ans){
ans=tmp;
anss[cnt++]=i;
}
else{
po[i]=po[i]^1;
}
}
printf("%d",cnt);
for(int i=0;i<cnt;i++){
printf(" %d",anss[i]);
}
printf("\n");
}

int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d",&n,&m);
int tmp;
for (int i=0; i<n; i++){
scanf("%d",&tmp);
po[i]=tmp;
}
int x,y;
tot=0;
memset(eh,-1,sizeof(eh));
for (int i=0; i<m; i++){
scanf("%d%d",&x,&y);
if (po[x]!=po[y]){
if (po[x]==0) addedge(x,y);
else addedge(y,x);
}
}
ans=Match();
solve();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: