您的位置:首页 > 产品设计 > UI/UE

POJ 1679 The Unique MST(次小生成树)

2012-10-03 15:35 507 查看
题目链接

题意:最小生成树是否唯一。

数据比较水,用最朴实的方法过的,O(n^2+e)再学一学,这个朴实方法改了N处,,思路简单,Kruskal写的,就是删除生成树里的每一条边,求最小生成树,唉,这是写的啥啊。。。。

#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
int o[101],num,sum,m,n;
struct edge
{
int sv;
int ev;
int w;
int flag;
} p[200001];
int cmp(const edge &a,const edge &b)
{
if(a.w < b.w)
return 1;
else
return 0;
}
int find(int x)
{
while(x != o[x])
x = o[x];
return x;
}
void merge(int id,int x,int y,int w,int z)
{
x = find(x);
y = find(y);
if(x != y)
{
sum += w;
o[x] = y;
if(z == -1)//第一次寻找最小生成树的时候记录下来
p[id].flag = 1;
num ++;
}
}
int kruskal(int x)
{
int i;
num = 0;
sum = 0;
for(i = 1; i <= n; i ++)
o[i] = i;
for(i = 1; i <= m; i ++)
{
if(i == x) continue;//删去边x的时候寻找最小生成树
if(num == n-1) break;
merge(i,p[i].sv,p[i].ev,p[i].w,x);
}
if(num == n-1)//特判是否存在最小生成树
return sum;
else
return -1;
}
int main()
{
int i,ans,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(i = 1; i <= m; i ++)
{
scanf("%d%d%d",&p[i].sv,&p[i].ev,&p[i].w);
p[i].flag = 0;
}
sort(p+1,p+m+1,cmp);
ans = kruskal(-1);//求最小生成树
int z = 1,temp;
for(i = 1; i <= m&&z; i ++)
{
if(p[i].flag == 1)//删除生成树中的边
{
temp = kruskal(i);//求次小生成树
if(ans == temp)
z = 0;
}
}
if(z)
printf("%d\n",ans);
else
printf("Not Unique!\n");
}
return 0;
}


O(n^2+e)做法就是看了一下有的kruskal先求出整个生成树,然后先用BFS预处理出生成树上,每一个点和另外的点之间最大的长度,Max[x][y],然后枚举不在生成树的边,min(ans-Max[x][y]+w(x,y))就是次小生成树了。感觉用kruskal比较麻烦,用prim写就比较方便了,可以一边处理,一边更新Max[x][y]。下边代码用prim写的,其主要的思想就是存起他的父亲节点,用生成树里的点到父亲的最大距离,与父亲到自己的距离比较,取大。

#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <algorithm>
#define N 100000000
using namespace std;
int p[201][201],low[201],o[201],dis[201][201],que[201],fath[201],key[201][201];
//dis数组存节点x到y的最大距离,que队列存进入生成树的节点
//fath存节点的父亲节点,key标记这条边是否在生成树上。
int Max(int a,int b)
{
return a > b ? a : b;
}
int main()
{
int i,j,k,n,m,t,sv,ev,w,ans,minz,temp;
scanf("%d",&t);
while(t--)
{
memset(p,0,sizeof(p));
memset(o,0,sizeof(o));
memset(dis,0,sizeof(dis));
memset(key,0,sizeof(key));
scanf("%d%d",&n,&m);
for(i = 1; i <= n; i ++)
{
for(j = 1; j <= n; j ++)
p[i][j] = N;
}
for(i = 1; i <= m; i ++)
{
scanf("%d%d%d",&sv,&ev,&w);
if(p[sv][ev] > w)
{
p[sv][ev] = w;
p[ev][sv] = w;
}
}
for(i = 1; i <= n; i ++)
{
low[i] = p[1][i];
}
o[1] = 1;
ans = 0;
int top;
top = 1;
que[1] = 1;
for(i = 1;i <= n;i ++)
{
fath[i] = 1;
}
for(i = 1; i <= n-1; i ++)
{
minz = N;
for(j = 1; j <= n; j ++)
{
if(minz > low[j]&&!o[j])
{
minz = low[j];
k = j;
}
}
key[fath[k]][k] = 1;
key[k][fath[k]] = 1;
if(minz == N) break;
o[k] = 1;
ans += minz;
for(j = 1; j <= top; j ++) //更新队列中的点到新加入的k的边上最大边,也就是dis数组
{
dis[que[j]][k] = dis[k][que[j]] = Max(minz,dis[que[j]][fath[k]]);
}
que[++top] = k;//k节点加入队列
for(j = 1; j <= n; j ++)
{
if(p[k][j] < low[j]&&!o[j])
{
low[j] = p[k][j];
fath[j] = k;//记录父亲
}
}
}
temp = N;
for(i = 1; i <= n; i ++)
{
for(j = 1; j <= n; j ++)
{
if(i != j&&p[i][j] != N&&!key[i][j])//枚举不在生成树的每一条边
{
if(temp > ans-dis[i][j]+p[i][j])
temp = ans-dis[i][j]+p[i][j];
}
}
}
if(temp == ans)
printf("Not Unique!\n");
else
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: