HDU3488 Tour KM

原文链接http://www.cnblogs.com/zhouzhendong/p/8284304.html


题目传送门 - HDU3488


题意概括

  给一个n的点m条边的有向图。

  然后让你把这个图分成许多环,问环中边权和最小为多少。

  题目保证一定存在合法的方案。


题解

  我们把每一个点扯成两个点。

  一个专门接受入度,一个专门接受出度,然后就是KM裸题了。

  数组模拟链表可能会比邻接矩阵快一些。


代码

#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstdio>
using namespace std;
const int INF=1e9+7;
const int N=205,M=30005;
struct Gragh{
	int cnt,y[M],z[M],nxt[M],fst[N];
	void clear(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b,int c){
		y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int T,n,m,match[N],ex[N],ey[N],minadd[N];
bool visx[N],visy[N];
bool Match(int x){
	visx[x]=1;
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (visy[y])
			continue;
		int add=ex[x]+ey[y]-g.z[i];
		if (!add){
			visy[y]=1;
			if (!match[y]||Match(match[y])){
				match[y]=x;
				return 1;
			}
		}
		else
			minadd[y]=min(minadd[y],add);
	}
	return 0;
}
int KM(){
	memset(match,0,sizeof match);
	memset(ey,0,sizeof ey);
	for (int i=1;i<=n;i++){
		ex[i]=-INF;
		for (int j=g.fst[i];j;j=g.nxt[j])
			ex[i]=max(ex[i],g.z[j]);
	}
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++)
			minadd[j]=INF;
		while (1){
			memset(visx,0,sizeof visx);
			memset(visy,0,sizeof visy);
			if (Match(i))
				break;
			int d=INF;
			for (int j=1;j<=n;j++)
				if (!visy[j])
					d=min(d,minadd[j]);
			for (int j=1;j<=n;j++){
				if (visx[j])
					ex[j]-=d;
				if (visy[j])
					ey[j]+=d;
				else
					minadd[j]-=d;
			}
		}
	}
	int ans=0;
	for (int i=1;i<=n;i++){
		int Max=-INF;
		for (int j=g.fst[match[i]];j;j=g.nxt[j])
			if (g.y[j]==i)
				Max=max(Max,g.z[j]);
		ans+=Max;
	}
	return ans;
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		g.clear();
		for (int i=1,a,b,c;i<=m;i++){
			scanf("%d%d%d",&a,&b,&c);
			g.add(a,b,-c);
		}
		printf("%d\n",-KM());
	}
	return 0;
}

  

 

posted @ 2018-01-14 20:49  zzd233  阅读(206)  评论(0编辑  收藏  举报