次小生成树

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是否唯一) 求出次小生成树,然后判断权值和是否等于最小生成树即可。  

#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;
}

转载于:https://www.cnblogs.com/AbandonZHANG/archive/2013/04/30/4114240.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值