题目描述
时间限制: 1000 ms 内存限制: 65536 kb
好久没见Thor了哈,他去了哪里呢?
他去了中世界,在给那里的城镇修铁路。
中世界铁路公司要求Thor建设这样一条铁路: 在连同所有城镇的前提下,使得铁路的总长最短。
这对于Thor自然不是难事咯——不就是求一个最小生成树嘛~
然而Thor是一个贪心的工程师,他想在这其中做一些手脚,使得既满足铁路的布局是一棵生成树,其费用又比最小生成树要大上那么一丢丢,这样他便可以从预算与实际的差价之中收取一定的私利。
然而Thor这样做还是有风险的,一旦被发现后果很严重。于是谨慎起见,他准备找到这样一个方案,在既使得布局是一颗生成树,又使得其费用是除最小生成树以外最小的费用。现在这个事情被交给了你。
输入
多组数据。
对于每一组数据,第一行有两个正整数n和m,表示有n个城镇以及m条可以铺设的铁路路线。(1≤n≤300)
接下来m行,每行有三个正整数x、y、w,表示城镇x到城镇y之间可以铺设一条长度为w的铁路。城镇编号从1开始。 (1≤c≤100)
数据保证没有重边和自环,同时保证图中有环存在。
输出
对于每一组数据,输出得到的生成树的边长之和。
输入样例
3 3
1 2 1
2 3 2
1 3 3
输出样例
4
思路1
先求出最小生成树,然后去掉最小生成树上的一条边,再求这个去掉了一条边的图最小生成树,这些最小生成树中的权值之和最小者(而且顶点必须仍然是n个的)就是原图的次小生成树。O(N3)O(N^3)O(N3)
代码
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef struct edge {
int u, v, w;
edge(int x, int y, int ww) : u(x), v(y), w(ww) {}
} edge;
bool cmp(edge a, edge b) {
return a.w < b.w;
}
int a[305][305], uf[305];
bool use[305][305];
int find(int num) {
if (uf[num] == num)return num;
return uf[num] = find(uf[num]);
}
void connect(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy) uf[fx] = fy;
}
int kruskal(int n) {
int edgeNum = 1;
memset(use, 0, sizeof(use));
vector<edge> vec;
vec.reserve(100000);
for (int x = 1; x <= n; x++)
for (int y = 1; y <= n; y++)
if (a[x][y] && x < y)
vec.emplace_back(x, y, a[x][y]);
sort(vec.begin(), vec.end(), cmp);
int sum = 0, cnt = 0;
for (int i = 1; i <= 300; i++)
uf[i] = i;
for (int i = 0; i < vec.size() && cnt < n - 1; i++)
if (find(vec[i].v) != find(vec[i].u)) {
sum += vec[i].w;
connect(vec[i].u, vec[i].v);
++cnt;
use[vec[i].u][vec[i].v] = use[vec[i].v][vec[i].u] = true;
++edgeNum;
}
return edgeNum == n ? sum : -1;
}
int main() {
int n, m, x, y, w;
while (~scanf("%d%d", &n, &m)) {
memset(a, 0, sizeof(a));
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &x, &y, &w);
a[x][y] = w;
a[y][x] = w;
}
kruskal(n);
vector<edge> mst;
mst.reserve(305);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (use[i][j] && i < j)
mst.emplace_back(i, j, a[i][j]);
int t, tst, max = 99999999;
for (auto &i : mst) {
t = i.w;
a[i.u][i.v] = a[i.v][i.u] = 0;
tst = kruskal(n);
if (tst < max && tst != -1)
max = tst;
a[i.u][i.v] = a[i.v][i.u] = t;
}
printf("%d\n", max);
}
}
思路2
求出最小生成树上任意两点的最短边,任意加入一条边之后必然成环,这时去掉这两点之间的最短边就会得到一棵树,这些树中权值最小的就是次小生成树。O(N2)O(N^2)O(N2)
代码
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
struct edge {
int head;
int tail;
int weight;
bool flag;
bool operator<(const edge &p) const {
return weight < p.weight;
}
};
struct mstNode {
int rank;
int weight;
int next;
};
struct node {
int rank;
int max; // 从某个点到它的路径中的最大边的长度
explicit node(int r = -1, int m = -1) : rank(r), max(m) {}
};
const int MAX = 100000;
edge edges[MAX];
mstNode msTree[602];
int n, m, num, p[MAX], maxWeight[301][301], Index[301];
void makeSet() {
for (int i = 0; i <= n; i++)
p[i] = i;
}
int findSet(int x) {
if (x != p[x])
p[x] = findSet(p[x]);
return p[x];
}
// 是从下向上建树的
void addEdge(int head, int tail, int weight) {
msTree[num].rank = tail;
msTree[num].weight = weight;
msTree[num].next = Index[head];
Index[head] = num++;
}
inline ll kruscal() {
int i, x, y, sum = 0;
makeSet();
sort(edges, edges + m);
for (i = 0; i < m; i++) {
x = findSet(edges[i].head);
y = findSet(edges[i].tail);
if (x != y) {
p[x] = y;
addEdge(edges[i].head, edges[i].tail, edges[i].weight);
addEdge(edges[i].tail, edges[i].head, edges[i].weight);
edges[i].flag = true;
sum += edges[i].weight;
}
}
return sum;
}
void bfs(int start) {
int i;
bool visited[301];
memset(visited, 0, sizeof(visited));
queue<node> que;
node now(start, 0);
node adj;
que.push(now);
visited[start] = true;
while (!que.empty()) {
node frontNode = que.front();
que.pop();
for (i = Index[frontNode.rank]; i != -1; i = msTree[i].next) {
adj.rank = msTree[i].rank;
adj.max = msTree[i].weight;
if (!visited[adj.rank]) {
if (frontNode.max > adj.max)
adj.max = frontNode.max;
maxWeight[start][adj.rank] = adj.max;
visited[adj.rank] = true;
que.push(adj);
}
}
}
}
ll secondaryMST() {
int i;
ll sum = kruscal();
ll secSum = 999999999;
ll tem;
for (i = 1; i <= n; i++)
bfs(i);
for (i = 0; i < m; i++)
if (!edges[i].flag) {
tem = sum + edges[i].weight - maxWeight[edges[i].head][edges[i].tail];
if (secSum > tem)
secSum = tem;
}
return secSum;
}
int main() {
while (~scanf("%d %d", &n, &m)) {
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &edges[i].head, &edges[i].tail, &edges[i].weight);
edges[i].flag = false;
}
num = 0;
memset(Index, -1, sizeof(Index));
printf("%lld\n", secondaryMST());
}
}