POJ 1679题

//题目大意:判断最小生成树是否唯一
//解题思路:求出最小的次小生成树,判断最小的次小生成树是否与最小生成树的权值和一样,如果一样,则唯一,否则不唯一
//求次小生成树的算法:
(1)有个比较直观的方法。依次把MST上的边删掉,然后再做MST。这样的做就是编码简洁,有模块可以重用。效率O(n*O(MST))
(2)既然每替换出去一条边,都将使代价变大。所以求次小生成树,只需要替换出去一条边即可。枚举不在树上的边eij,树上路径上的边权都不大于w[i][j]。
选取其中最大的与eij替换,得到新树的权重。选取替换后最小的一个就是次小生成树了。这个过程可以做到O(m).然后就是计算出path_max[i][j]表示i,j树上路径中的最大边权。
在计算MST的时候,点是依次加入到树中。当前加入的是j,与他相连的是i。那么,对所有在树上集合中的k:path_max[k][j] = max{ path_max[k][i] , w[i][j] }
因为path_max[][]的每个元素都要计算,且仅一次。累加起来的复杂度是O(n^2).总的复杂度:O(MST)+O(n^2)+O(m)
最小生成树的详细解释见:http://hi.baidu.com/hplonline/blog/item/56fab312cc7677c7c2fd7819.html
#include <stdio.h>
#include <algorithm>
using namespace std;
#define arraysize 101
typedef struct node
{
 int v;
 int u;
 int w;
}node;
node edges[arraysize*arraysize];
int r[arraysize];
int parent[arraysize];
int n,m;
int flag[arraysize];   //记录最小生成树中使用到的边
int maxdata = 0x7fffffff;
bool cmp(node a,node b)
{
 return a.w<b.w;
}
int findroot(int n)
{
 if(parent[n] == n)
  return n;
 else
  parent[n] = findroot(parent[n]);
 return parent[n];
}
void init()
{
 for(int i3=1;i3<n+1;++i3)
 {
  parent[i3] = i3;
  r[i3] = 1;
 }
}
int main()
{
 //freopen("1.txt","r",stdin);
 int t;
 while(scanf("%d",&t)!=EOF)
 {
  for(int i=0;i<t;++i)
  {
   //求最小生成树
   int sum = 0;   //记录最小生成树的权值和
   scanf("%d%d",&n,&m);
   int start,end,weight;
   for(int i1=1;i1<m+1;++i1)
   {
    scanf("%d%d%d",&start,&end,&weight);
    edges[i1].v = start;
    edges[i1].u = end;
    edges[i1].w = weight;
   }
   sort(edges+1,edges+m+1,cmp);  //此处排序时注意排序的范围
   init();
   int p=0;
   for(int i2=1;i2<m+1;++i2)   //遍历生成树中的所有边
   {
    int a = edges[i2].v;
    int b = edges[i2].u;
    int roota = findroot(a);
    int rootb = findroot(b);
    if(roota!=rootb)
    {
     p++;
     flag[p] = i2;    //技巧:记录最小生成树中使用到了的边
     if(r[roota]>=r[rootb])
     {
      parent[rootb] = roota;
      r[roota] += r[rootb];
     }
     else
     {
      parent[roota] = rootb;
      r[rootb] += r[roota];
     }
     sum += edges[i2].w;
    } 
    if(p==n-1)      //最小生成树生成完毕
     break;
   }
   int mincisum = maxdata;    //记录次小生成树中的最小树的权值和
   int cisum = 0;      //记录次小生成树的权值和
   //依次删除最小生成树中的边,从图中剩下的所有边中再求最小生成树
   for(int i4=1;i4<n;++i4)   
   {
    int edgenum = 0;
    init();
    cisum  = 0;
    for(int i5=1;i5<m+1;++i5)  //遍历所有的边
    {
     if(flag[i4] == i5)   //删除某一条边
      continue;
     int a = edges[i5].v;
     int b = edges[i5].u;
     int roota = findroot(a);
     int rootb = findroot(b);
     if(roota!=rootb)
     {
      edgenum++;
      if(r[roota]>=r[rootb])
      {
       parent[rootb] = roota;
       r[roota] += r[rootb];
      }
      else
      {
       parent[roota] = rootb;
       r[rootb] += r[roota];
      }
      cisum += edges[i5].w;
     }     
     if(edgenum == n-1)   //次小生成树生成完毕
      break;
    }
    if(edgenum == n-1 && cisum < mincisum) //求所有次小生成树中的最小树
     mincisum = cisum;
   }
   if(mincisum !=sum)  //判断次小生成树中的最小树是否与最小生成树的权值和相等
   {
    printf("%d\n",sum);
   }
   else
    printf("Not Unique!\n");
  }
 }
 return 0;
}

你可能感兴趣的:(poj)