#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID(x,y) ((x+y)>>1)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
const int sup = 0x7fffffff;
const int inf = -0x7fffffff;
const int MAXN = 103;
const int MAXE = 30003;
/* 链式前向星 */
struct node{ //gragh node
int u, v, w;
int op; //无向边拆成两个有向边对应的另一个编号
bool in_MST; //是否在最小生成树中;
int next;
}edge[MAXE];
int cnt, head[MAXN];
void Init(){
cnt = 0;
mem(head, -1);
}
void Add_edge(int u, int v, int w){ //添加无向边
edge[cnt].in_MST = false;
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
edge[cnt].op = cnt + 1;
head[u] = cnt ++;
edge[cnt].in_MST = false;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].w = w;
edge[cnt].next = head[v];
edge[cnt].op = cnt - 1;
head[v] = cnt ++;
}
/* 链式前向星 */
struct prim_node{ //prim node
int t; //状态节点标号
int id; //第几个边
int w; //权值
friend bool operator < (prim_node n1, prim_node n2){
return n1.w > n2.w;
}
void Init(int n){
id = -1;
t = n;
w = sup;
}
}dist[MAXN];
bool vis[MAXN];
priority_queue > Q;
int MST_NUM;
void Prim(int start, int n){ //初始MST_NUM=0,则对于不连通的图MST_NUM=0;
mem(vis, 0);
for (int i = 0; i <= n; i ++)
dist[i].Init(i);
while(!Q.empty())
Q.pop();
dist[start].w = 0;
Q.push(dist[start]);
while(!Q.empty()){
prim_node tmp = Q.top();
Q.pop();
int u = tmp.t;
int w = tmp.w;
int id = tmp.id;
if (vis[u]) continue;
vis[u] = true;
if (id != -1){ //确定最小生成树的边和权值和
edge[id].in_MST = true;
edge[edge[id].op].in_MST = true;
MST_NUM += w;
}
for (int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
int cost = edge[i].w;
if (!vis[v] && dist[v].w > cost){
dist[v].w = cost;
dist[v].id = i;
Q.push(dist[v]);
}
}
}
}
int max_cost_on_MST[MAXN][MAXN];
bool used[MAXN];
void dfs(int s, int t, int maxnum){
max_cost_on_MST[s][t] = maxnum;
used[t] = 1;
for (int i = head[s]; i != -1; i = edge[i].next){
if (edge[i].in_MST == false)
continue;
int v = edge[i].v;
if (!used[v]) dfs(s, v, max(maxnum, edge[i].w));
}
}
int n, m;
void Second_MST(){
Prim(1, n);
int res = sup;
//枚举不在最小生成树中的边
for (int i = 1; i <= n; i ++){
mem(used, 0);
dfs(i, i, 0);
for (int j = head[i]; j != -1; j = edge[j].next){
int v = edge[j].v;
if (edge[j].in_MST == false)
res = min(res, MST_NUM + edge[j].w - max_cost_on_MST[i][v]);
}
}
if (res == MST_NUM){
printf("Not Unique!\n");
}
else{
printf("%d\n", MST_NUM);
}
}
int main(){
int t;
scanf("%d", &t);
while(t --){
MST_NUM = 0;
Init();
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i ++){
int a, b, l;
scanf("%d %d %d", &a, &b, &l);
Add_edge(a, b, l);
}
Second_MST();
}
return 0;
}
K小生成树: 生成树T删除一条边f并加入一条新边e的操作称为
交换。若交换后的图仍是一颗树,则此交换称为
可行交换。若生成树T可通过一次交换成为生成树T’,则称它们互为
邻树。对于生成树集合S,生成树T,若T不在S中,且在S中存在某生成树是T的邻树,称为T为S的邻树。 定理:设T1, T2, …… , TK为图的前k小生成树,则生成图集合{T1, T2, …… , TK}的邻树中的边权和最小者可作为第k+1小生成树(可能有边权和相同的情况)。 按这个定理设计算法,很难得到有满意的时间复杂度的算法。 下面讨论一个特例:
次小生成树(The second MST, 2-MST)
基本思想:首先求出最小生成树,记录权值之和为MST_NUM。然后枚举添加边(u,v),加上以后一定形成一个环,找到环上非(u,v)边的权值最大的边,把它删掉,计算当前生成树的权值之和,取所有枚举加边后生成树权值之和的最小值,就是次小生成树。
算法: 1.求出最小生成树T及其权值和MST_NUM,并标注在最小生成树上的边。 2.从每个顶点i为根,DFS遍历最小生成树,求出从i到j的路径上最大边的权值P(i, j)。 3.遍历每条不在最小生成树中的边(i,j),加上这条边,并删除环上最大边(P(i,j)),新的生成树权值之和为MST_NUM + w(i,j) - P[i][j],记录其最小值即可,时间复杂度为O(N^2)。求最小生成树可以用最简单的Prim即可,算法的瓶颈在第二步DFS遍历求路径上最大边需要O(n^2),用更好的算法是没有意义的。(
PS:对于每个边,实际上可以用LCA和预处理来得到环内最大边,这样复杂度就是O(ElogV),然后配合Kruskal复杂度可以降到O(ElogV), 留待以后学习。)
练习题:
POJ 1679 The Unique MST (MST是否唯一) 求出次小生成树,然后判断权值和是否等于最小生成树即可。
转载于:https://www.cnblogs.com/AbandonZHANG/archive/2013/04/30/4114240.html