题意
给一张图包括各个城市、可以使用的和已经被破坏的公路。
当某个城市被占领后,为了保持其他城市的联通需要修路,要求就是输出花费最多的城市。注意:如果修路了也不能使剩余城市联通,那么这个城市就一定不能被占领
考点
并查集
思路
对除去某顶点的图求最小生成树,边的数量是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;
}