懒得找最小生成树模板了,就把这题当板子吧。
最小生成树,就是指对于一张图,我们将图转换成一棵树,连通的,同时让所有的边尽可能的小(废话)。
最小生成树一般都采用Kruskal算法,期间需要用到并查集。大体思路如下:
先将所有边从小到大排序,对所有的节点维护并查集 f。
然后依次遍历所有的边,(当然是先从小的开始)。可以将并查集的 f 理解为缩点的类似操作。如果一条边的两个点
在同一个 f 中,则证明这两个点已经连通,不需要这条边(当然是前面通过别的小边已经连通了)。
因此,在从小到大遍历边的时候,如果两个点不在一个集合,就连这条边(因为他小)。
在同一个集合中,就不用画蛇添足了。
再看这道(没人权的)题,如果把每个物品当作点,那么价格就是边权。如果有优惠,我们当然选择买优惠的。(虽然这道题数据毒瘤,居然还有
优惠比原价高的情况,必须特判)
没有优惠,就连原价。反正最后都会经过最小生成树的筛选。
上板子代码:
#include <bits/stdc++.h> using namespace std; #define N 1000100 #define isdigit(c) ((c)>='0'&&(c)<='9') #define orz 0 inline int read(){ int x = 0, s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = (x << 1) + (x << 3) + (c ^ '0'); c = getchar(); } return x * s; } struct node{ int u, v, w; } t[N]; int bian = 0; int fa[N]; int ans = 0; int find(int x){ return x == fa[x] ? x : fa[x] = find(fa[x]); } bool cmp (node a, node b){ return a.w < b.w; } void kruskal(int pri, int n){ for(int i = 1;i <= n; i++) fa[i] = i; sort(t + 1, t + bian + 1, cmp); for(int i = 1;i <= bian; i++){ int u = t[i].u, v = t[i].v, w = t[i].w; int fau = find(u), fav = find(v); if(fau != fav){ fa[fau] = fav; ans += (w < pri ? w : pri); /*毒瘤数据可能出现 w > pri 的情况,这种时候肯定选择 pri*/ } } return ; } int main(){ // freopen("hh.txt", "r", stdin); int pri = read(), n = read(); for(int i = 1;i <= n; i++){ for(int j = 1;j <= n; j++){ int x = read(); if(x != 0){ /*注意给边去重!!*/ t[++bian].u = i; t[bian].v = j; t[bian].w = x; } } } kruskal(pri, n); for(int i = 1;i <= n; i++) if(fa[i] == i)ans += pri; /*可能出现有的物品没有优惠或者是图中的第一个*/ printf("%d ", ans); return orz; /*向dalao低头*/ }