HDU 5438 Ponds 拓扑序+并查集/DFS

12 篇文章 0 订阅

就是给一个图,每个点有权值,边没有。然后会先依次删除度数<=1的点,一直删到不能删为止。然后求所有连通块中节点数为奇数的连通块的权值和的和。

首先删点就是拓扑序来删,然后求连通块可以DFS或者BFS或者并查集都可以。

训练时刚结束后,队友改了改,过的,直接邻接表(当然是vector存),拓扑序+BFS求连通块过掉的。

嗯,然后,去网上学了一发链式前向星,然后并查集搞连通块的,但我不清楚那些人用了链式前向星了,还要用两个数组来保存所有的边在并查集的时候用,链式前向星就可以遍历所有边啊,复杂度也是一样的。反正窝把存边的两个数组删掉后也A了。嗯,不管他们了。

代码:

#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 10003
#define maxm 100003
struct Edge
{
	int next, to, w;
	Edge() :next(0), to(0), w(0) { }
}edge[maxm];
int head[maxn], cnt = 0;
int val[maxn], degree[maxn];
bool vis[maxn];//记录该点是否被访问
int n, m;
int set[maxn];
int size[maxn];//结点数
long long sum[maxn];
void init_set()
{
	for (int i = 1; i<maxn; ++i)
	{
		set[i] = i;
		size[i] = 1;
		sum[i] = val[i];
	}
}
int findSet(int x)//路径压缩
{
	if (x == set[x])
		return x;
	else
		return set[x] = findSet(set[x]);
}
void unionSet(int x, int y)//启发式合并
{
	int fx = findSet(x);
	int fy = findSet(y);
	if (fy == fx)
		return;
	if (size[fx] >= size[fy])
	{
		size[fx] += size[fy];
		set[fy] = fx;
		sum[fx] += sum[fy];
	}
	else
	{
		size[fy] += size[fx];
		set[fx] = fy;
		sum[fy] += sum[fx];
	}
}
void add(int u, int v, int w = 1)
{
	edge[cnt].w = w;
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
void topo_del()
{
	queue<int> que;
	for (int i = 1; i <= n; ++i)
	{
		if (degree[i] <= 1)
		{
			degree[i] = 0;
			vis[i] = false;
			que.push(i);
		}
	}
	while (!que.empty())
	{
		int x = que.front();
		que.pop();
		for (int i = head[x]; i != 0; i = edge[i].next)
		{
			int y = edge[i].to;
			if (vis[y])
			{
				degree[y]--;
				if (degree[y] == 1)
				{
					vis[y] = false;
					que.push(y);
				}
			}
		}
	}
}
int main()
{
	//freopen("input.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	while (T--)
	{
		long long ans = 0;
		scanf("%d%d", &n, &m);
		Edge etmp;
		cnt = 1;
		memset(vis, true, sizeof(bool)*maxn);
		memset(degree, 0, sizeof(int)*maxn);
		memset(head, 0, sizeof(int)*maxn);
		memset(val, 0, sizeof(int)*maxn);
		for (int i = 0; i < maxm; ++i)
			edge[i] = etmp;
		for (int i = 1; i <= n; ++i)
			scanf("%d", &val[i]);
		int a, b;
		for (int i = 0; i < m; ++i)
		{
			scanf("%d%d", &a, &b);
			add(a, b);
			add(b, a);
			degree[a]++;
			degree[b]++;
		}
		topo_del();
		init_set();
		for (int i = 1; i <= n; ++i)
		{
			for (int j = head[i]; j != 0; j = edge[j].next)
				if (vis[i] && vis[edge[j].to])
					unionSet(i, edge[j].to);
		}
		for (int i = 1; i <= n; ++i)
			if (vis[i] && set[i] == i&&size[i] & 1)
				ans += sum[i];
		printf("%I64d\n", ans);
	}
	//while (1);
	//system("pause");
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值