hdu 3488 Tour(有向带权最小环覆盖)

题目链接

题意

给定一张有向图,每条边有权值,求用1个或多个不相交的环覆盖所有点并且环的权值和最小

题解

  • 与有向图最小路径覆盖类似,考虑将有向图拆点成二分图,由于最后所有点都被环覆盖,所以最后的环覆盖一定对应拆点二分图上的一组完备匹配。
  • 于是问题就转化成了求二分图带权最优匹配问题,由于满足最优匹配一定是完备匹配所以可以用$KM$,或者直接跑费用流也行
  • 贴个大佬的bfs写法稳定$O(n^3)$的$KM$板子
查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 400+5;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
struct KM
{
	int n;
	int mat[maxn][maxn];               //边权
	int matcha[maxn], matchb[maxn];   //左边的点匹配的右边点;右边的点匹配的左边点
	int marka[maxn], markb[maxn];      //左顶标;右顶标
	int slack[maxn];                   //松弛数组
	bool visa[maxn], visb[maxn];      //访问标记
	int head, tail;
	int q[maxn], pre[maxn];           //队列;交错路径
	bool check(int cur)
	{
		visb[cur]=true;     //标记cur已搜索
		if (matchb[cur])    //已匹配,即当前匹配失败
		{
			if (!visa[matchb[cur]])    //匹配的点是否已进队
			{
				q[++tail]=matchb[cur];
				visa[matchb[cur]]=true;
			}
			return false;
		}
		//未匹配,即当前匹配成功,沿交错路径进行匹配
		while (cur)
			swap(cur, matcha[matchb[cur]=pre[cur]]);
		return true;
	}

	void bfs(int start)
	{
		fill(visa, visa+1+n, false);
		fill(visb, visb+1+n, false);
		fill(slack, slack+1+n, inf);
		head=tail=1;
		q[1]=start;
		visa[start]=true;
		while (1)
		{
			while (head<=tail)
			{
				int cur=q[head++];
				for (int i=1; i<=n; ++i)
				{
					int diff=marka[cur]+markb[i]-mat[cur][i];
					if (!visb[i] && diff<=slack[i])   //visb=true说明已搜索,无需更新slack和pre,也是保证pre的正确性
					{
						slack[i]=diff;
						pre[i]=cur;
						if (diff==0)  //diff=0,可以尝试匹配
							if (check(i)) return; //匹配成功可直接返回
					}
				}
			}
			int delta=inf;
			for (int i=1; i<=n; ++i)
				if (!visb[i] && slack[i]) delta=min(slack[i], delta);
			for (int i=1; i<=n; ++i)    //松弛
			{
				if (visa[i]) marka[i]-=delta;
				if (visb[i]) markb[i]+=delta;
				else slack[i]-=delta;   //维护slack的正确性(参考diff的计算及marka,markb的变化)
			}
			head=1, tail=0;
			for (int i=1; i<=n; ++i)
				if (!visb[i] && !slack[i] && check(i)) return;
				//松弛后尝试匹配diff=0的点。
		}
	}
	int solve()
	{
		fill(matcha, matcha+1+n, 0);
		fill(matchb, matchb+1+n, 0);
		fill(markb, markb+1+n, 0);
		for (int i=1; i<=n; ++i)
		{
			marka[i]=0;
			for (int j=1; j<=n; ++j)
				marka[i]=max(marka[i], mat[i][j]);
		}
		for (int i=1; i<=n; ++i) bfs(i);
		int ans = 0;
		for(int i = 1;i <= n;++i){
			ans+=mat[matchb[i]][i];
		}
		return -ans;
	}
}km;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("simple.in", "r", stdin);
    freopen("simple.out", "w", stdout);
#endif
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m);
		memset(km.mat,-inf,sizeof(km.mat));
		km.n=n;
		for(int i = 1,a,b,c;i <= m;++i){
			scanf("%d%d%d",&a,&b,&c);
			km.mat[a][b]=max(km.mat[a][b],-c);
		}
		printf("%d\n",km.solve());
	}
    return 0;
}

posted @ 2020-06-03 21:30  tryatry  阅读(165)  评论(0编辑  收藏  举报