郁闷两天,先是写了200多行的kruskal ,后改为prim算法,只有90行,程序变简洁了,速度变快了n倍。
这是kruskal算法,由于kruskal算法适用于稀疏图,故运行时间很慢
虽然我已经用O(n^2)的时间求得了所用两点之间的唯一的路径上最大权
值的边。
方法:
Compute max(u; v) for all vertices in T. Compute for any edge (u; v) not in T the difference
w(u; v) - max(u; v). The two edges yielding the smallest positive difference should be replaced.
代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define BUFSIZE 555
#define ULTIMATE 100000000
int set[BUFSIZE];
int height[BUFSIZE];
void merge(int a,int b)
{
if(height[a]==height[b]){
height[a]++;
set[b]=a;
}else {
if(height[a]>height[b])
set[b]=a;
else set[a]=b;
}
}
int find(int x)
{
int r,i,j;
r=x;
while(set[r]!=r)r=set[r];
i=x;
while(i!=r){
j=set[i];
set[i]=r;
i=j;
}
return r;
}
int N;
struct edges{
int a;
int b;
int v;
};
vector<edges> t,r;
int Max[BUFSIZE][BUFSIZE];
struct Edge{
int v;
int value;
Edge *next;
};
struct Node{
int v;
bool visited;
Edge *link;
};
Node G[BUFSIZE];
FILE *fin;
FILE *fout;
void inittree()
{
for(int i=1;i<=N;i++)
G[i].link = NULL;
for(int i=0;i<t.size();i++){
Edge *p = new Edge;
p->v = t[i].a;
p->value = t[i].v;
p->next = G[t[i].b].link;
G[t[i].b].link = p;
p = new Edge;
p->v = t[i].b;
p->value = t[i].v;
p->next = G[t[i].a].link;
G[t[i].a].link = p;
}
}
int parent[BUFSIZE];
void dfs(int i)
{
Edge *p = G[i].link;
G[i].visited = true;
while(p!=NULL){
if(!G[p->v].visited){
parent[p->v] = i;
dfs(p->v);
}
p = p->next;
}
}
int stack[BUFSIZE];
int top;
void dfs2(int i)
{
Edge *p = G[i].link;
G[i].visited = true;
stack[top++] = i;
while(p!=NULL){
if(!G[p->v].visited){
for(int j=0;j<top;j++){
Max[stack[j]][p->v] = max(p->value,Max[stack[j]][parent[p->v]]);
Max[p->v][stack[j]] = Max[stack[j]][p->v];
}
dfs2(p->v);
}
p = p->next;
}
}
void findmax()
{
fill_n(&Max[0][0],BUFSIZE*BUFSIZE,0);
dfs(1);
top = 0;
for(int i=1;i<=N;i++)
G[i].visited = false;
dfs2(1);
}
int SUM;
int Min;
void secondmin()
{
inittree();
findmax();
Min = ULTIMATE;
for(int i=0;i<r.size();i++){
int m = r[i].v - Max[r[i].a][r[i].b];
if(m>=0)
Min = min(Min,m);
}
}
bool comp(edges a,edges b)
{
return a.v<b.v;
}
edges edge[BUFSIZE];
void span()
{
int m;
fscanf(fin,"%d%d",&N,&m);
for(int i=1;i<=N;i++)
set[i] = i;
fill_n(height,BUFSIZE,0);
for(int i=0;i<m;i++)
fscanf(fin,"%d%d%d",&edge[i].a,&edge[i].b,&edge[i].v);
sort(edge,edge+m,comp);
for(int i=0;i<m;i++){
int p = find(edge[i].a);
int q = find(edge[i].b);
if(p!=q){
t.push_back(edge[i]);
SUM += edge[i].v;
merge(p,q);
}else
r.push_back(edge[i]);
}
}
int main()
{
fin = stdin;
fout = stdout;
span();
secondmin();
fprintf(fout,"Cost: %d/n",SUM);
if(Min == ULTIMATE)
fprintf(fout,"Cost: -1/n");
else
fprintf(fout,"Cost: %d/n",SUM+Min);
// scanf("%d",&N);
return 0;
}
由于运行慢,这种方法不可取。
想到了写用heap优化的prim算法用邻接表作为存储结构
有写了200多行,后来发现用邻接矩阵会好一些
这是我用邻接矩阵写的程序:
#include<iostream>
#include<algorithm>
using namespace std;
#define BUFSIZE 555
#define ULTIMATE 2000000000
struct Node{
int parent;
int key;
}node[BUFSIZE];
int N,M,SUM,Min;;
int Max[BUFSIZE][BUFSIZE];
int Graph[BUFSIZE][BUFSIZE];
int stack[BUFSIZE],top;
void init()
{
fill_n(&Graph[0][0],BUFSIZE*BUFSIZE,ULTIMATE);
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++){
int s,d,v;
scanf("%d%d%d",&s,&d,&v);
Graph[s][d] = v;
Graph[d][s] = v;
}
}
bool comp(Node a,Node b)
{
if(a.key){
if(b.key)
return a.key<=b.key;
else return true;
}
return false;
}
void prim()
{
SUM = 0;
for(int j=1;j<N;j++){
node[j].parent = N;
node[j].key = Graph[N][j];
}
node[N].key = 0;
for(int i=1;i<N;i++){
Node *p = min_element(node+1,node+1+N,comp);
int k = p - node;
for(int j=0;j<top;j++){
Max[stack[j]][k] = max(Max[stack[j]][p->parent],p->key);
Max[k][stack[j]] = Max[stack[j]][k]; //这点很重要,要求v到u(u已经在集合中)的路径上的最大权值边
} //只需要取v的父亲的Max[u][v'parent] 和value(u,v)的大的那个。
// 这样在prim生成树的过程中就求得所有点的Max[u][v]。
SUM += Graph[p->parent][k];
stack[top++] = k;
p->key = 0; //表示点p已经加入了生成树的集合。
Graph[k][p->parent] = Graph[p->parent][k] = ULTIMATE;//表示边k---p->parent不在树上。
for(int j=1;j<N;j++)
if(Graph[k][j]<node[j].key){
node[j].parent = k;
node[j].key = Graph[k][j];
}
}
}
void secondmin()
{
Min = ULTIMATE;
for(int i=1;i<=N;i++)
for(int j=i;j<=N;j++){
int m = -1;
if(Graph[i][j]!=ULTIMATE)
m = Graph[i][j] - Max[i][j]; //只要交换使得m取最小非负值的两条边,便得到次小生成树。
if(m>=0)
Min = min(Min,m);
}
printf("Cost: %d/n",SUM);
if(Min == ULTIMATE)
printf("Cost: -1/n");
else
printf("Cost: %d/n",SUM+Min);
}
int main()
{
init();
prim();
secondmin();
// scanf("%d",&N);
return 0;
}
调用c++的min_element只比用binary heap差一点点!!!
上面的kruskal算法同prim的求次小生成树的原理一样的,只不过具体实现麻烦一点!
求次小生成树!
最新推荐文章于 2020-11-29 08:43:46 发布