首页 > 代码库 > mst1679

mst1679

 

 

判断生成树的唯一性,唯一则输出权值,不唯一输出Not Unique

次小生成树权值是否等于最小生成树的

 

一种容易想到的方法是枚举删除最小生成树上的边,再求最小生成树。用kruskal这种算法的复杂度为O(n*elog2e),当图比较稠密时,复杂度接近O(n^3)

但有一种更简单的方法:先求最小生成树T,枚举添加不在T中的边,则添加后一定会形成环。找到环上边值第二大的边(即环中属于T中的最大边,新添加进去的肯定最大,毕竟T是最小生成树,T之外的边最大),把它删掉,计算当前生成树的权值,取所有枚举修改的生成树的最小值,即为次小生成树。

 

 

    这种方法在实现时有更简单的方法:首先求最小生成树T,然后从每个结点u遍历最小生成树T,用一个二维数组max[u][v]记录结点u到结点v的路劲上(不是指UV节点权值是MAX[U][V],而是UV路径上(在最小生成树上)最大权值边是max[u][v]边的最大值(即最大边的值)。然后枚举不在T中的边(u,v),计算T- max[u][v] + w(u,v)的最小值,即为次小生成树的权值。显然,这种方法的时间复杂度为O(n^2 + e)

可见,第二种算法将原来的时间复杂度O(n^3)提高到了O(n^2)

#include <iostream>

#include <algorithm>

#include <queue>

const int INF = 0x7fffffff;

const int MAX = 10001;

int n,m;

int num;

int p[MAX];

int max[101][101];

 

struct Edge //原始图

{

int from;

int to;

int w;

bool flag;

}e[MAX];

 

struct Tree //最小生成树

{

int to;

int w;

int next;

}tree[202];

int index[101];

 

struct Node //生成树的结点

{

int seq; //结点编号

int max; //从某个点到它的路径中的最大边的长度

};

 

bool cmp(const Edge &a, const Edge &b)

{

return a.w < b.w;

}

 

void makeSet()

{

for(int i = 0; i <= n; i++)

{

p[i] = i;

}

}

 

int findSet(int x)

{

if(x != p[x])

p[x] = findSet(p[x]);

return p[x];

}

 

void addEdge(int from, int to, int w)

{

tree[num].to = to;

tree[num].w = w;

tree[num].next = index[from];

index[from] = num++;

}

 

int kruscal()

{

int i,j;

int x, y;

int edgeNum = 0;

int result = 0;

makeSet();

std::sort(e,e+m,cmp);

for(i = 0; i < m; i++)

{

x = findSet(e[i].from);

y = findSet(e[i].to);

if(x != y)

{

edgeNum++;

addEdge(e[i].from,e[i].to,e[i].w);

addEdge(e[i].to,e[i].from,e[i].w);

e[i].flag = true;

p[x] = y;

result += e[i].w;

}

}

return edgeNum == n-1 ? result : -1;

}

 

void bfs(int p)

{

int i,j;

bool used[101];

memset(used,0,sizeof(used));

std::queue<Node> que;

Node now,adj;

now.max = 0;

now.seq = p;

que.push(now);

used[p] = true;

while(!que.empty())

{

Node q = que.front();

que.pop();

for(i = index[q.seq]; i != -1; i = tree[i].next)//在最小生成树上找

{

adj.seq = tree[i].to;

adj.max = tree[i].w;

if(!used[adj.seq])

{

if(q.max > adj.max)

adj.max = q.max;

max[p][adj.seq] = adj.max;

used[adj.seq] = true;

que.push(adj);

}

}

}

}

 

void second_MST()

{

int i,j;

int mst = kruscal();

for(i = 1; i <= n; i++)

bfs(i);

int smst = INF;

for(i = 0; i < m; i++)

{

if(!e[i].flag)

{

if(mst + e[i].w - max[e[i].from][e[i].to] < smst)

smst = mst + e[i].w - max[e[i].from][e[i].to];

/*

这里,遍历所有不在最小生成树的边。

如果它起点在最小生成树上,而另一个端点不再最小生成树上,那么max[e[i].from][e[i].to] =0,则mst + e[i].w - max[e[i].from][e[i].to] 得到的肯定是偏大的值。

如果他两个端点都不在生成树上,那么同样max[e[i].from][e[i].to] =0

只有两个端点U,V都在生成树上,且边不属于最小生成树的边,他们的max[e[i].from][e[i].to]不等于0,才有可能mst + e[i].w - max[e[i].from][e[i].to] 最小。对于这种情况,加上之后,树就变成图了,UV之间存在两条路径相连,因此要减去UV上原先在最小生成树的边,从这其中,删除最大的那条

 

*/

}

}

if(smst == mst)

printf("Not Unique!\n");

else

printf("%d\n",mst);

}

 

int main()

{

int i,j;

int cases;

int a,b,w;

scanf("%d",&cases);

while(cases--)

{

scanf("%d %d",&n,&m);

for(i = 0; i < m; i++)

{

scanf("%d %d %d",&e[i].from,&e[i].to,&e[i].w);

e[i].flag = false;

}

num = 0;

memset(index,-1,sizeof(index));

second_MST();

}

return 0;

}

mst1679