秘密的牛奶运输 题解

题目连接

分析

一道可以暴力水过去的次小生成树

  • step1

首先用 K r u s k a l Kruskal Kruskal|| P r i m Prim Prim求出原图的一颗最小生成树,在连边的时候,用一个 v i s vis vis记录一下那些已经在最小生成树里面。

  • step2

提前暴力 d f s dfs dfs或者 b f s bfs bfs求出任意两点构成的环之间的最大权值

具体操作

定义函数 d f s ( i n t s , i n t u , i n t f a t h e r , i n t m w 1 , i n t m w 2 ) dfs(int s, int u, int father, int mw1, int mw2) dfs(ints,intu,intfather,intmw1,intmw2)
s -> 起点 u -> 终点 father 判断是否回到原起点 mw1 最大值 mw2 次大值
再用md1[s][u]保存从s -> u的最大边权值, 用md2[s][u]保存从s -> u的次大边权值
循环遍历与重点u相连接的每一个点(v),如果没有回到father就继续。
定义t1, t2
如果 从u -> v的 权值(w) 大于了现在的mw1
- 把t1 更新为 w
- 把t2 更新为 mw1
否则 如果 w 大于了现在的mw2 并且小于 mw1
- 把t1 更新为 mw1
- 把t2 更新为 w
最后递归遍历 dfs(s, v, u, t1, t2)

  • step2

再一次考虑每一条非树边,用这条边上的权值考虑替换原边

END

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 505;
const int MAXM = 1e4 + 5;

long long n, m, tot, fa[MAXN], md1[MAXN][MAXN], md2[MAXN][MAXN];
long long ans, sum;
bool vis[MAXN];

struct node {
    long long u, v, w;
    bool vis;
} dis[MAXM];
bool cmp(node x, node y) {
    return x.w < y.w;
}

struct edge {
    int v, w;
    edge() {}
    edge(int V, int W) {
        v = V;
        w = W;
    }
};

vector<edge> G[MAXN];

void AddEdge(int u, int v, long long w) { G[u].push_back(edge(v, w)); }

int FindSet(int v) {
    if (fa[v] == v)
        return fa[v];
    else
        return fa[v] = FindSet(fa[v]);
}
bool UnionSet(int u, int v) {
    int x = FindSet(u);
    int y = FindSet(v);
    if (x == y)
        return 0;
    else {
        fa[x] = fa[y];
        return 1;
    }
}

void dfs(int s, int u, int fa, int mw1, int mw2) {
	md1[s][u] = mw1;
	md2[s][u] = mw2;
	for (int j = 0; j < G[u].size(); j++) {
		int v = G[u][j].v;
		int w = G[u][j].w;
		if (v != fa) {
			int t1, t2;
			if (w > mw1) {
				t1 = w;
				t2 = mw1;
			} else if (w < mw1 && w > mw2) {
				t1 = mw1;
				t2 = w;
			}
			dfs (s, v, u, t1, t2);
		}
	}
} 
void Kruskal() {
    for (int i = 1; i <= n; i++) fa[i] = i;
    sort(dis + 1, dis + 1 + m, cmp);
    for (int i = 1; i <= m; i++) {
        if (UnionSet(dis[i].u, dis[i].v)) {
            sum += dis[i].w;
            tot++;
            dis[i].vis = 1;
            AddEdge(dis[i].u, dis[i].v, dis[i].w);
            AddEdge(dis[i].v, dis[i].u, dis[i].w);
        }
        if (tot == n - 1)
            break;
    }
    for (int i = 1; i <= n; i++) {
    	dfs (i, i, -1, 0, 0);
    }
    ans = 1e19;
    for (int i = 1; i <= m; i++) {
	if (dis[i].vis == 0) {
		int w = dis[i].w, u = dis[i].u, v = dis[i].v;
		if (w > md1[u][v]) ans = min(ans, sum + w - md1[u][v]);
		else if (w > md2[u][v]) ans = min(ans, sum + w - md2[u][v]);
	}
    }
    printf ("%lld\n", ans);
}

int main() {
    scanf("%lld %lld", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf ("%lld %lld %lld", &dis[i].u, &dis[i].v, &dis[i].w);
    }
    Kruskal();
    return 0;
}

你可能感兴趣的:(图论,最小生成树)