zoukankan      html  css  js  c++  java
  • 「图论」第2章 最小生成树课堂过关

    「图论」第2章 最小生成树课堂过关

    A. 【例题1】繁忙都市

    题目

    代码

    prim

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define N 310
    #define M 200010
    struct Heap {
    #define max_(_ , __) ((_) < (__) ? (_) : (__))
    	int siz;
    	int a[M * 2] , b[M * 2];
    	bool ty;
    	inline void swap_(int x , int y) {
    		int tmp;
    		tmp = a[y] , a[y] = a[x] , a[x] = tmp;
    		tmp = b[y] , b[y] = b[x] , b[x] = tmp;
    	}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    		memset(b , 0 , sizeof(b));
    	}
    	inline void push(int dat , int dat2) {
    		a[++siz] = dat;
    		b[siz] = dat2;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);
    		--siz;
    		int p = 1 , tmp;
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty))
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top_A() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline int top_B() {
    		return siz == 0 ? 0 : b[1];
    	}
    	inline bool empty() {
    		return siz == 0;
    	}
    } h;
    
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    int nxt[M] , val[M] , to[M];
    int head[N];
    bool vis[N];
    inline void addedge(int u , int v , int value) {
    	static int cnt = 0;
    	++cnt;
    	val[cnt] = value , to[cnt] = v , nxt[cnt] = head[u] , head[u] = cnt;
    }
    
    int n , m;
    int ans;
    int main() {
    	n = read() , m = read();
    	for(int i = 1 ; i <= m ; i++) {
    		int u , v , value;
    		u = read() , v = read() , value = read();
    		addedge(u , v , value);
    		addedge(v , u , value);
    	}
    
    	for(int i = head[1] ; i ; i = nxt[i])
    		h.push(val[i] , to[i]);
    	vis[1] = true;
    	for(int j = 2 ; j <= n ; j++) {
    		while(vis[h.top_B()] && !h.empty())
    			h.pop();
    		if(h.empty())	break;
    		int u = h.top_B();
    		if(h.top_A() > ans)
    			ans = h.top_A();
    		h.pop();
    		
    		vis[u] = true;
    		for(int i = head[u] ; i ; i = nxt[i]) {
    			if(!vis[to[i]]) {
    				h.push(val[i] , to[i]);
    			}
    		}
    	}
    	printf("%d %d" , n - 1 , ans);
    	return 0;
    }
    

    Kruskal

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define N 310
    #define M 100010
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    struct ednode{
    	int u , v , val;
    }ed[M];
    bool cmp(ednode a , ednode b) {
    	return a.val < b.val;
    }
    int fa[N];
    int findroot(int x) {
    	return fa[x] == x ? x : (fa[x] = findroot(fa[x]));
    }
    void uni(int x , int y) {
    	fa[findroot(x)] = findroot(y);
    }
    
    int n , m;
    int ans;
    int main() {
    	n = read() , m = read();
    	for(int i = 1 ; i <= m ; i++)
    		ed[i].u = read() , ed[i].v = read() , ed[i].val = read();
    
    	sort(ed + 1 , ed + m + 1 , cmp);
    	for(int i = 1 ; i <= n ; i++)
    		fa[i] = i;
    	for(int i = 1 ; i <= m ; i++) {
    		if(findroot(ed[i].u) != findroot(ed[i].v)) {
    			uni(ed[i].u , ed[i].v);
    			if(ed[i].val > ans)
    				ans = ed[i].val;
    		}
    	}
    	printf("%d %d" , n - 1 , ans);
    	return 0;
    }
    

    B. 【例题2】新的开始

    题目

    思路

    想象一个原点,它到(i)点的距离就是(i)建发电站的费用,其余连边不变,从那个原点开始跑一边最小生成树即可

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define N 310
    #define M N * N
    struct Heap {
    #define max_(_ , __) ((_) < (__) ? (_) : (__))
    	int siz;
    	int a[M * 2] , b[M * 2];
    	bool ty;
    	inline void swap_(int x , int y) {
    		int tmp;
    		tmp = a[y] , a[y] = a[x] , a[x] = tmp;
    		tmp = b[y] , b[y] = b[x] , b[x] = tmp;
    	}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    		memset(b , 0 , sizeof(b));
    	}
    	inline void push(int dat , int dat2) {
    		a[++siz] = dat;
    		b[siz] = dat2;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);
    		--siz;
    		int p = 1 , tmp;
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty))
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top_A() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline int top_B() {
    		return siz == 0 ? 0 : b[1];
    	}
    	inline bool empty() {
    		return siz == 0;
    	}
    } h;
    
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    int n , m;
    int ans;
    int map[N][N];
    bool vis[N];
    int main() {
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		h.push(read() , i);
    	for(int i = 1 ; i <= n ; i++)
    		for(int j = 1 ; j <= n ; j++) {
    			map[i][j] = read();
    		}
    
    	for(int j = 1 ; j <= n ; j++) {
    		while(vis[h.top_B()] && !h.empty())
    			h.pop();
    		if(h.empty())	break;
    		int u = h.top_B();
    		ans += h.top_A();
    		h.pop();
    
    		vis[u] = true;
    		for(int i = 1 ; i <= n ; i++) {
    			if(!vis[i])
    				h.push(map[i][u] , i);
    		}
    	}
    	printf("%d" , ans);
    	return 0;
    }
    

    C. 【例题3】公路建设

    题目

    思路

    题目的意思是给定(m)条边,依次加入一开始没有边的图(G)中,若当前加入的边不能使(G)强连通,输出"0",否则,输出图(G)的最小生成树

    我们把边依次加入一条链中,按插入排序的思想维护链的有序性,跑一次Kruskal就可以求出当前的最小生成树

    时间复杂度(O(ncdot m))

    代码

    #include <iostream>
    #include <cstdio>
    #define N 510
    #define M 2010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    struct ednode {
    	int x , y , val , nxt;
    }ed[M];
    int head = 1;
    void insert(int x , int y , int val) {
    	static int cnt = 0;
    	++cnt;
    	ed[cnt].x = x , ed[cnt].y = y , ed[cnt].val = val;
    	if(cnt == 1)	return;
    	int p = head;
    	if(val < ed[p].val) {
    		ed[cnt].nxt = head , head = cnt;
    		return;
    	}
    	for( ; ed[p].nxt != 0 ; p = ed[p].nxt)
    		if(ed[p].val <= val && val <= ed[ed[p].nxt].val) {
    			ed[cnt].nxt = ed[p].nxt , ed[p].nxt = cnt;
    			return;
    		}
    	ed[p].nxt = cnt;
    	ed[cnt].nxt = 0;
    	return;
    }
    
    int n , m;
    int fa[N];
    int findroot(int x) {
    	return fa[x] == x ? x : (fa[x] = findroot(fa[x]));
    }
    void uni(int x , int y) {
    	fa[findroot(x)] = findroot(y);
    }
    
    
    int main() {
    	n = read() , m = read();
    	for(int i = 1 ; i <= m ; i++) {
    		int u = read() , v = read() , val = read();
    		insert(u , v , val);
    		
    		for(int j = 1 ; j <= n ; j++)
    			fa[j] = j;
    			
    		int ans = 0;
    		for(int j = head ; j ; j = ed[j].nxt)
    			if(findroot(ed[j].x) != findroot(ed[j].y)) {
    				uni(ed[j].x , ed[j].y);
    				ans += ed[j].val;
    			}
    		int ty = true;
    		int sta = findroot(1);
    		for(int i = 1 ; i <= n ; i++)
    			if(findroot(i) != sta) {
    				ty = false;
    				break;
    			}
    		if(!ty)
    			puts("0");
    		else
    			printf("%.1f
    " , ans * 0.5);
    	}
    	
    	return 0;
    }
    

    D. 【例题4】构造完全图

    题目

    思路

    50分

    有一个贪心:如果(u)(v)路径上最大边权为(val),则(u),(v)之间连一条边权小于等于(val)的边,原最小生成树会改变,否则不会改变.具体的证明可以从边权及边的数量出发,这里不再赘述

    因此,直接枚举每个点,各跑一边深搜遍历整棵树即可,时间复杂度(O(n^2))

    100分

    从数据规模可以看到,(n)去到(10^5),即边的数量可以达到(10^{10}),一条一条算肯定是不行的.

    考虑对于最小生成树的每一条边(ed_i),它连接两个连通块(T_1),(T_2),大小为(val),它一定是连接(T_1),(T_2)所有边中最小的,设(T_1)中有(siz_1)个节点,(T_2)中有(siz_2)个节点,则除了(ed_i),一共有(siz_1 cdot siz_2-1)条,每条(除(ed_i)外)的最小权值为(val+1)

    更具体地,代码解释吧

    代码

    50分

    #include <iostream>
    #include <cstdio>
    #define ll long long
    #define N 100010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    
    struct ednode {
    	int to , val , nxt;
    }ed[N * 2];
    int head[N];
    inline void addedge(int x , int y , int v) {
    	static int cnt = 0;
    	++cnt;
    	ed[cnt].to = y , ed[cnt].val = v , ed[cnt].nxt = head[x] , head[x] = cnt;
    	return;
    }
    
    int n;
    ll ans;
    
    void dfs(int x , int maxv , int fa , int dep) {
    	if(dep > 1)			ans += maxv + 1;
    	for(int i = head[x] ; i ; i = ed[i].nxt) {
    		if(ed[i].to == fa)	continue;
    		dfs(ed[i].to , maxv > ed[i].val ? maxv : ed[i].val , x , dep + 1);
    	}
    }
    
    int main() {
    	n = read();
    	for(int i = 1 ; i < n ; i++) {
    		int u = read() , v = read() , val;
    		ans += (val = read()) * 2;
    		addedge(u , v , val);
    		addedge(v , u , val);
    	}
    	for(int i = 1 ; i <= n ; i++) {
    		dfs(i , -1 , i , 0);
    	}
    	cout << ans / 2;
    	return 0;
    }
    

    100分

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9')
    		c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    
    struct ednode{
    	int u , v , val;
    }ed[N * 2];
    bool cmp(ednode a , ednode b) {
    	return a.val < b.val;
    }
    
    int fa[N] , siz[N];
    int findroot(int x) {
    	return x == fa[x] ? x : (fa[x] = findroot(fa[x]));
    }
    int n;
    long long ans;
    int main() {
    	n = read();
    	for(int i = 1 ; i < n ; i++)
    		ed[i].u = read() , ed[i].v = read() , ans += (ed[i].val = read());
    		
    	sort(ed + 1 , ed + n , cmp);
    	for(int i = 1 ; i <= n ; i++)
    		fa[i] = i , siz[i] = 1;
    	
    	for(int i = 1 ; i < n ; i++) {
    		int u = findroot(ed[i].u) , v = findroot(ed[i].v);
    		if(u != v) {
    			ans += (long long)(siz[u] * siz[v] - 1) * (ed[i].val + 1);
    			fa[u] = v;
    			siz[v] += siz[u];
    		}
    	}
    	cout << ans;
    	return 0;
    }
    

    随机数据生成

    #include <bits/stdc++.h>
    using namespace std;
    int random(int r , int l = 1) {
    	return (long long) rand() * rand() % (r - l + 1) + l; 
    }
    pair <int,int> ed[1000010];
    int dict[1000010];//用于打乱结点编号,有些有向图不适用
    int main() {
    	unsigned seed;
    	cin >> seed;
    	seed *= time(0);
    	srand(seed);//外界输入种子
    	
    	int n = random(1e4 , 5);
    	for(int i = 1 ; i <= n ; i++)
    		dict[i] = i;
    	random_shuffle(dict + 1 , dict + n + 1);
    	for(int i = 1 ; i < n ; i++) {
    		ed[i].first = random(i);
    		ed[i].second = i + 1;
    	}
    	printf("%d
    " , n);
    	
    	random_shuffle(ed + 1 , ed + n);//随机打乱
    	for(int i = 1 ;i < n ; i++)
    		printf("%d %d %d
    " , dict[ed[i].first] , dict[ed[i].second] , random(1e5));
    	
    	return 0;
    }
    
    
  • 相关阅读:
    Java--Filter(过滤器)
    TP5.1验证Token和Electron-vue头部携带Token
    TP5.1让验证码在另外的项目(Electron-vue)里面使用
    Electron-vue请求携带cookie跨域问题
    Electron-vue在发送请求时携带cookie
    TP5.1解决跨域
    Electron-vue解决跨域
    Electron-vue运行之后出现了文件浏览器
    Electron-vue取消代码检查Eslint
    使用Composer安装TP5.1出现zsh: no matches found: 5.1.*
  • 原文地址:https://www.cnblogs.com/dream1024/p/14670990.html
Copyright © 2011-2022 走看看