hdu 3435 A new Graph Game (KM ,二分图最优匹配)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3435

An undirected graph is a graph in which the nodes are connected by undirected arcs. An undirected arc is an edge that has no arrow. Both ends of an undirected arc are equivalent--there is no head or tail. Therefore, we represent an edge in an undirected graph as a set rather than an ordered pair.
Now given an undirected graph, you could delete any number of edges as you wish. Then you will get one or more connected sub graph from the original one (Any of them should have more than one vertex).
You goal is to make all the connected sub graphs exist the Hamiltonian circuit after the delete operation. What’s more, you want to know the minimum sum of all the weight of the edges on the “Hamiltonian circuit” of all the connected sub graphs (Only one “Hamiltonian circuit” will be calculated in one connected sub graph! That is to say if there exist more than one “Hamiltonian circuit” in one connected sub graph, you could only choose the one in which the sum of weight of these edges is minimum).
  For example, we may get two possible sums:


(1)  7 + 10 + 5 = 22
(2)  7 + 10 + 2 = 19
(There are two “Hamiltonian circuit” in this graph!)

 

 

Input

In the first line there is an integer T, indicates the number of test cases. (T <= 20)
In each case, the first line contains two integers n and m, indicates the number of vertices and the number of edges. (1 <= n <=1000, 0 <= m <= 10000)
Then m lines, each line contains three integers a,b,c ,indicates that there is one edge between a and b, and the weight of it is c . (1 <= a,b <= n, a is not equal to b in any way, 1 <= c <= 10000)

 

 

Output

Output “Case %d: “first where d is the case number counted from one. Then output “NO” if there is no way to get some connected sub graphs that any of them exists the Hamiltonian circuit after the delete operation. Otherwise, output the minimum sum of weight you may get if you delete the edges in the optimal strategy.
 

 

 

Sample Input

 

3 3 4 1 2 5 2 1 2 2 3 10 3 1 7 3 2 1 2 3 1 2 4 2 2 1 2 3 1 2 4

 

 

Sample Output

 

Case 1: 19 Case 2: NO Case 3: 6

 

同hdu 3488 hdu 1853 。有向环覆盖问题。

注意建图时候,建双向边,但是不是无向图。

#pragma GCC optimize(2)
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<set>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1500;
const int inf = 0x3f3f3f3f;
typedef long long ll;
int mp[maxn][maxn], linker[maxn], lx[maxn], ly[maxn];
int  slack[maxn];
bool visx[maxn], visy[maxn];
int n, m, nx, ny;
int t;
bool dfs(int x)
{
	visx[x] = true;
	for (int y = 1; y <= ny; y++)
	{
		if (visy[y])
		{
			continue;
		}
		int tmp = lx[x] + ly[y] - mp[x][y];
		if (tmp == 0)
		{
			visy[y] = true;
			if (linker[y] == -1 || dfs(linker[y]))
			{
				linker[y] = x;
				return true;
			}
		}
		else if (slack[y] > tmp)
		{
			slack[y] = tmp;
		}
	}
	return false;
}
void km()
{
	memset(linker, -1, sizeof(linker));
	memset(ly, 0, sizeof(ly));
	for (int i = 1; i <= nx; i++)
	{
		lx[i] = -inf;
		for (int j = 1; j <= ny; j++)
		{
			if (mp[i][j] > lx[i])
			{
				lx[i] = mp[i][j];
			}
		}
	}
	for (int x = 1; x <= nx; x++)
	{
		for (int j = 1; j <= ny; j++)
		{
			slack[j] = inf;
		}
		while (1)
		{
			memset(visx, false, sizeof(visx));
			memset(visy, false, sizeof(visy));
			if (dfs(x))
			{
				break;
			}
			int d = inf;
			for (int i = 1; i <= n; i++)
			{
				if (!visy[i])
				{
					d = min(d, slack[i]);
				}
			}
			for (int i = 1; i <= n; ++i)
			{
				if (visx[i])
				{
					lx[i] -= d;
				}
				if (visy[i])
				{
					ly[i] += d;
				}
				else
				{
					slack[i] -= d;
				}
			}
		}
	}
}
int main()
{
	//freopen("C://input.txt", "r", stdin);
	scanf("%d", &t);
	int pos = 1;
	while (t--)
	{
		scanf("%d%d", &n, &m);
		nx = ny = n;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				mp[i][j] = -inf;
			}
		}
		for (int i = 1; i <= m; i++)
		{
			int u, v, c;
			scanf("%d%d%d", &u, &v, &c);
			if (u == v)
			{
				continue;
			}
			if (-c > mp[u][v])
			{
				mp[v][u] = mp[u][v] = -c;
			}
		}
		km();
		printf("Case %d: ", pos++);
		int ans, cnt;
		ans = cnt = 0;
		for (int i = 1; i <= ny; i++)
		{
			if (linker[i] == -1 || mp[linker[i]][i] == -inf)
			{
				continue;
			}
			ans += mp[linker[i]][i];
			cnt++;
		}
		if (cnt != nx)
		{
			printf("NO\n");
		}
		else
		{
			printf("%d\n", -ans);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值