PAT T1001 Battle Over Cities - Hard Version (35分)

题意

给一张图包括各个城市、可以使用的和已经被破坏的公路。
当某个城市被占领后,为了保持其他城市的联通需要修路,要求就是输出花费最多的城市。注意:如果修路了也不能使剩余城市联通,那么这个城市就一定不能被占领

考点

并查集

思路

对除去某顶点的图求最小生成树,边的数量是N-2(除去了被攻占的城市)。
类似于Prime算法,注意边的排序,首先的是边是否可用,其次的才是边的花费
例如:
1 2 1 1
2 3 2 1
2 4 1 0
3 4 2 0
然后不断挑选判断,最后判断满足条件的边是否有N-2条即可
有三个判断:
如果该边的端点包括被攻占的顶点,那么该边不被算在内
如果该边的端点已经属于一个集合了,说明两个端点在一个连通图中,因此该边也可以不算在内
如果该边的端点不属于一个集合,此时要合并集合并判断是否需要费用
最后判断,如果符合条件的边没有N-2条,那么属于无论怎么修路都无法使所有城市连通,被攻占的城市就特别重要,因此可以设置它的花费为无穷大,重要性最高
如果所有的顶点删除后剩余的都连通,那么输出0

代码

#include <iostream>
using namespace std;
#include <set>
#include <algorithm>
#define maxn 250001
#define inf 0x7fffffff

typedef struct node
{
	int c1, c2, cost, status;
}node;

node pn[maxn];
int n, m;//顶点数和边数
int father[maxn];
int f_cost = 0;//记录最大的花费
set<int> ans;//用set可以将结果自动从小到大排序

bool cmp(node a, node b)
{
	if (a.status == b.status)
		return a.cost < b.cost;
	else
		return a.status > b.status;
}

int findfather(int x)
{
	if (x == father[x])
		return x;
	else
	{
		int v = findfather(father[x]);
		father[x] = v;
		return v;
	}
}


int main()
{
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> pn[i].c1 >> pn[i].c2 >> pn[i].cost >> pn[i].status;
	}
	sort(pn, pn + m, cmp);
	for (int i = 1; i <= n; i++)
	{
		//初始化
		for (int j = 1; j <= n; j++)
			father[j] = j;
		int cost = 0;
		int num = n - 2;
		//遍历边
		for (int j = 0; j < m; j++)
		{
			if (pn[j].c1 == i || pn[j].c2 == i)//端点包含被排除的点
				continue;
			int faA = findfather(pn[j].c1);
			int faB = findfather(pn[j].c2);
			if (faA == faB)//端点已经属于一个连通图了
				continue;
			//该边有用
			father[faA] = faB;
			num--;
			if (pn[j].status == 0)
				cost += pn[j].cost;
			
		}
		if (num > 0)
			cost = inf;
		if (cost > f_cost)
		{
			f_cost = cost;
			ans.clear();
			ans.insert(i);
		}
		else if (cost == f_cost && cost !=0)
			ans.insert(i);
	}
	if (ans.size() == 0)
		cout << "0";
	else
		for (auto it = ans.begin(); it != ans.end(); it++)
			if (it == ans.begin())
				cout << *it;
			else
				cout << " " << *it;
	return 0;
}

结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值