来源:http://poj.org/problem?id=1679
题意:判断最小生成树是否唯一。
思路:求出最小生成树后再求次小生成树,如果次小生成树的长度和最小生成树的长度相等,则最小生成树不唯一,否则最小生成树唯一。
介绍一下我求次小生成树的方法。
次小生成树可以用kruskal和prime两种方法求。kruskal算法求的过程就是多次求最小生成树。我们第一次求最小生成树的时候把边的序号都标记下来,然后以后多次求最小生成树的时候,每次都不用第一次求得最小生成树的时候的边。比如说,第一次求最小生成树用到了边1 6 7,则我们再求3次最小生成树,第一次不用边1,第二次不用边6,第三次不用边7.。这样求三次,若中间有哪一次和第一次的的和相等,则说明不唯一。否则,唯一。这道题的话,倘若用kruskal算法的话,复杂度是2亿,会超。这道题可以用prime算法。
用prime算法求次小生成树的过程是先求一遍最小生成树,在求得过程中我们设置一个pre数组,表示的是某个点的前驱,比如说刚开始选到点1,然后选到的第一条边是从1到3的,则pre[3] = 1,并且在求最小生成树的过程中把选到的边记录下来。我们再设置另一个数组maxedge,表示在构造好的树中,如果x和y之间有一条边,且该边没有在最小生成树中,则maxedge[x][y] 表示加上这条边后,所能去掉的最大的边。比如说,我们求出一颗最小生成树后,此时x和y有一条边,且这条边没有被用到,此时maxedge[x][y]表示生成树中从x到y的路径中的最大的一条边,也就是说连接x和y的路径中能够去掉的最大的边。也就是说求出最小生成树后,在遍历图中所有的没有用到的边求次小生成树即可。如果某个值和最小生成树的值相同,则最小生成树不唯一。否则最小生成树唯一。
代码:
//1679
#include <iostream>
#include <cstdio>
#include <climits>
#include <string.h>
using namespace std;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
const int N = 110;
int vis[N],map[N][N],numpoint,numroad,use[N][N],maxedge[N][N];
int len1,len2,pre[N];
int max(int a,int b){
return a > b ? a:b;
}
int min(int a,int b){
return a < b ? a:b;
}
void prime(){
int dis[N],temp,pos = 0;
CLR(vis,0);
CLR(maxedge,0);
for(int i = 1; i <= numpoint; ++i){
dis[i] = map[1][i];
pre[i] = 1;
}
dis[1]= 0;
vis[1] = 1;
len1 = 0;
for(int i = 1; i <= numpoint; ++i){
temp = INT_MAX;
for(int j = 1; j <= numpoint; ++j){
if(temp > dis[j] && !vis[j]){
temp = dis[j];
pos = j;
}
}
if(temp == INT_MAX)
break;
vis[pos] = 1;
pre[pos] = i;
use[i][pos] = use[pos][i] = 0;
len1 += temp;
for(int j = 1; j <= numpoint; ++j){
if(vis[j])
maxedge[j][pos] = maxedge[pos][j] = max(maxedge[pre[pos]][j],map[pre[pos]][pos]);
if(!vis[j]){
if(dis[j] > map[pos][j]){
dis[j] = map[pos][j];
pre[j] = pos;
}
}
}
}
}
void second_prime(){
len2 = INT_MAX;
for(int i = 1; i <= numpoint; ++i){
for(int j = 1; j <= numpoint; ++j){
if(use[i][j]){
len2 = min(len2,len1 + map[i][j] - maxedge[i][j]);
}
}
}
}
int main(){
//freopen("1.txt","r",stdin);
int numcase;
scanf("%d",&numcase);
while(numcase--){
for(int i = 0; i < N; ++i)
for(int j = 0; j < N; ++j)
map[i][j] = INT_MAX;
CLR(use,0);
CLR(maxedge,0);
CLR(pre,0);
scanf("%d%d",&numpoint,&numroad);
int x,y,z;
for(int i = 0; i < numroad; ++i){
scanf("%d%d%d",&x,&y,&z);
map[x][y] = map[y][x] = z;
use[x][y] = use[y][x] = 1;
}
prime();
second_prime();
if(len1 == len2)
printf("Not Unique!\n");
else{
printf("%d\n",len1);
}
}
return 0;
}