次小生成树

次小生成树:把所有生成树按照权值之和从小到大的顺序排列,求排在第二位的生成树。

次小生成树可能也是最小生成树;

次小生成树可以通过有最小生成树换一条边得到;

次小生成树的求法:时间复杂度为:V^2(V为节点数)

(1)、用prim算法求出最小生成树T,在prim的同时,用一个矩阵max_val[u][v]记录在T中连接任意两点u,v的唯一路中权值最大的那条边的权值;

求max_val[u][v]:

            在prim算法中,设已经被标记的节点集合为W,每次增加一个接点s的时候,容易求出所有节点到s的路中的最大边权值:

            设u属于W,且s是被连到W中的v点,则有:

                   max_val[v][s] = 边(v,s)的值;

                   max_val[u][s] = max(max_val[u][v],max_val[v][s]);

(2)、枚举所有不在T中的边(u,v),加入边(u,v)则必然替换权值为max_val[u][v]的边,枚举一次就得到一颗新的生成树,如果在这些生成树中,有权值和原最小生树相等的,则最小生成树不唯一,其中权值最小的一颗生成树就是次小生成树;

其中max_val[][] 的求法是维护信息代替重新计算,能够减少不必要的计算,降低时间复杂度,是很好的解题思想;

poj 2831次小生成树模板;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<map>
#define ll long long
#define MAX 101000
#define MAXN  1010
#define INF 2000000000
#define eps 1e-8

using namespace std;

struct Edge{
	int from,to,dist;
}E[MAX];

struct Node{
	int u,w,pre;
	bool operator < (const Node& rhs) const{
		return rhs.w < w;
	}
};

vector<int>G[MAXN];
vector<Edge>edges;

int n;

void init(){
	for (int i=0; i<=n; i++) G[i].clear();
	edges.clear();
}

void addEdge(int from, int to, int dist){
	edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);
}

int max_val[MAXN][MAXN];

void prim(int s){
	int d[MAXN],vis[MAXN],p[MAXN],val[MAXN];   //p[]记录生成树中与该节点相连的节点,val[]记录已经被标记的节点
	priority_queue<Node>q;
	
	int cnt = 0;
	memset(max_val,0,sizeof(max_val));
	memset(vis,0,sizeof(vis));
	memset(p,-1,sizeof(p));
	for (int i=0; i<=n; i++) d[i] = (i == s ? 0 : INF);
	
	q.push((Node){s,0,-1});	
	while (!q.empty()){
		Node t = q.top();
		q.pop();
		if (vis[t.u]) continue;
		int v = t.u;
		vis[v] = 1;
		if (v != s){       //当加入的节点不是根节点时,更新相应max_val[][]
			max_val[t.pre][v] = max_val[v][t.pre] = t.w;
			for (int i=0; i<cnt; i++) max_val[val[i]][v] = max_val[v][val[i]] = max(max_val[val[i]][t.pre], t.w);
		}
		val[cnt++] = v;
		for (int i=0; i<G[v].size(); i++){
			Edge e = edges[G[v][i]];
			if (!vis[e.to]){
				if (d[e.to] > e.dist){
					d[e.to] = e.dist;
					p[e.to] = v;
				}
				q.push((Node){e.to,d[e.to],p[e.to]});
			}
		}
	}
}



int main(){
	int m,Q;
	while (scanf("%d%d%d",&n,&m,&Q) != EOF){
		int a,b,c;
		init();
		for (int i=0; i<m; i++){
			scanf("%d%d%d",&a,&b,&c);
			addEdge(a,b,c);
			addEdge(b,a,c);
			E[i] = (Edge){a,b,c};
		}
		prim(1);
		for (int i=0; i<Q; i++){
			scanf("%d%d",&a,&b);
			int u = E[a-1].from;
			int v = E[a-1].to;
			if (b <= max_val[u][v]){
				printf("Yes\n");
			}
			else{
				printf("No\n");
			}
		}
	}
	return 0;
}

poj1679 次小生成树模板;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<map>
#define ll long long
#define MAX 20000
#define MAXN  110
#define INF 2000000000
#define eps 1e-8

using namespace std;

struct Edge{
	int from,to,dist;
};

struct Node{                            //注意优先队列要维护的节点信息,和最小生成树的不同之处
	int u,w,pre,edg;
	bool operator < (const Node& rhs) const{
		return rhs.w < w;
	}
};

vector<int>G[MAXN];
vector<Edge>edges;

int n;

void init(){
	for (int i=0; i<=n; i++) G[i].clear();
	edges.clear();
}

void addEdge(int from, int to, int dist){
	edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);
}

int max_val[MAXN][MAXN],ep[MAXN];  //ep[]记录最小生成树中与节点相连的边的编号,即记录最小生成树中的边(次小生成树要中未被标记的边枚举)

int prim(int s){
	int d[MAXN],vis[MAXN],p[MAXN],val[MAXN];     //数组作用同上
	priority_queue<Node>q;
	
	int cnt = 0,ans = 0;
	memset(max_val,0,sizeof(max_val));
	memset(vis,0,sizeof(vis));
	memset(p,-1,sizeof(p));
	memset(ep,-1,sizeof(ep));
	for (int i=0; i<=n; i++) d[i] = (i == s ? 0 : INF);
	
	q.push((Node){s,0,-1,-1});	
	while (!q.empty()){
		Node t = q.top();
		q.pop();
		if (vis[t.u]) continue;
		ans += t.w;
		int v = t.u;
		vis[v] = 1;
		if (v != s){
			max_val[t.pre][v] = max_val[v][t.pre] = t.w;
			for (int i=0; i<cnt; i++) max_val[val[i]][v] = max_val[v][val[i]] = max(max_val[val[i]][t.pre], t.w);
		}
		val[cnt++] = v;
		for (int i=0; i<G[v].size(); i++){
			Edge e = edges[G[v][i]];
			if (!vis[e.to]){
				if (d[e.to] > e.dist){
					d[e.to] = e.dist;
					p[e.to] = v;
					ep[e.to] = G[v][i];
				}
				q.push((Node){e.to,d[e.to],p[e.to],ep[e.to]});
			}
		}
	}
	return ans;
}

int mark[MAX];

bool check(){
	memset(mark,0,sizeof(mark));     
	for (int i=1; i<=n; i++){
		if (ep[i] != -1){
			mark[ep[i]] = 1;
			mark[ep[i]^1] = 1;
		}
	}
	for (int i=0; i < edges.size(); i++){
		if (!mark[i]){
			Edge e = edges[i];
			if (e.dist == max_val[e.from][e.to]) return true;
		}
	}
	return false;
}

int main(){
	int m,T;
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		int a,b,c;
		init();
		for (int i=0; i<m; i++){
			scanf("%d%d%d",&a,&b,&c);
			addEdge(a,b,c);
			addEdge(b,a,c);
		}
		int ans = prim(1);
		if (!check()){
			printf("%d\n",ans);
		}
		else printf("Not Unique!\n");
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值