您的位置:首页 > 其它

简单图论-最小生成树—kruskal+prim

2010-01-03 13:16 316 查看
所以prim是每次是选点进集合,而kruskal是选边进集合(稀疏的时候很有用)

另外这两个算法只用于无向图的,双向不同的值是不能用这两个算法的

有向的要使用貌似一个叫有向树形图的算法

kruskal hdu1102 没AC

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>

using namespace std;

struct edge
{
int a;
int b;
int value;
};

int comp(const void *a,const void *b)
{
edge *t1=(edge*)a;
edge *t2=(edge*)b;
return t1->value - t2->value ;
}

//kruskal
//n是点,m是边
//时间复杂度是 mlog(m)+m*n
//前面是由于排序的关系
//后面是因为每次用贪心取最小的边,但是取了这个边不能构成环
//要取n-1次,所以有这个时间复杂度

class ckruskal
{
private:
static const int MAX=200;
static const int INF=0x7fffffff;
int dis[MAX][MAX];
int set[MAX];
edge len[MAX]; //边数组
int sig[MAX][MAX];
int n,m; //n个点,m条边
int rm;
public:
void input();
void output();
int kruskal();
void solve();
int find(int t);
};

int ckruskal::find(int t)
{
int i,tt=t;
while(set[t]!=t)
t=set[t];

while(tt!=t) //压缩
{ i=set[tt];
set[tt]=t;
tt=i;
}
return t;
}

void ckruskal::solve()
{
input();
}

void ckruskal::input()
{
int i,j,tm;
while(scanf("%d",&n)!=EOF){
m=0; //这里不是稀疏矩阵,所以导致这种情况 m的值很大
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ scanf( "%d",&dis[i][j] );
if(!dis[i][j])
dis[i][j]=INF;
if( i<j )
{ len[m].a=i;len[m].b=j;len[m].value=dis[i][j];m++;}
}

memset(sig,0,sizeof(sig));
for(i=1;i<=n;i++)
set[i]=i; //并查集初始化
int a,b;
scanf("%d",&tm);
rm=0;
for(i=1;i<=tm;i++)
{ scanf("%d %d",&a,&b);
sig[a][b]=1; //这条边已经连过了
sig[b][a]=1;
a=find(a);
b=find(b);
if(a!=b)
{ set[a]=b;
rm++;
}
}
rm=n-1-rm;
output();

}
}

int ckruskal::kruskal()
{
int i=0,cnt;
int ll=0;
qsort(len,m,sizeof(edge),::comp); //排序,每次取最小的

for(cnt=1;cnt<=rm;) //n-1条边就是最小生成树了,这里是特殊情况
{
if( sig[len[i].a][len[i].b] )
{
i++;
continue;
}

int t1=find(len[i].a);
int t2=find(len[i].b);
if(t1==t2)
{
i++;
continue;
}

set[t1]=t2;

ll+=len[i].value;
cnt++;
i++;
}

return ll;

}

void ckruskal::output()
{
printf("%d/n",kruskal());
}

int main()
{
ckruskal kl;
kl.solve();

return 0;
}

hdu 1875 PRIM算法 AC

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace std;

//n^2的时间复杂度
//思想是 把所有点集 分成lh和rh
//lh是已经选入集合的点 rh是集合外的点
//从rh中选择 lh和rh边最短的边 的点
//然后更新 lh到rh的各点的边长。
//hoho~经典啊

class cprim
{
private:
static const int MAX=400;
static const int INF=0x7fffffff;
int sig[MAX];
int dis[MAX][MAX];
int t,n,m;

public:
int prim();
void input();
inline void output()
{
printf("%d/n",prim());
}

};

void cprim::input()
{
int a,b,c;
while(scanf("%d",&n) && n!=0)
{ memset(sig,0,sizeof(sig));
for(a=1;a<=n;a++)
dis[a][a]=INF;
m=n*(n-1)/2;
while(m--)
{
scanf("%d %d %d",&a,&b,&c);
dis[a][b]=c;
dis[b][a]=c;
}
output();
}
}

int cprim::prim()
{
int min=INF;
int lh[MAX]; //left_hand的array,代表当前选进点组成的所有点到点的距离

int i,idx,len=0,cnt=1;
if( n==1 )
return 0;

idx=1;sig[idx]=1; //选入lh;
for(i=1;i<=n;i++)
lh[i]=INF;
while( cnt<n )
{
for(i=1;i<=n;i++)
{
if( dis[idx][i]<lh[i] && !sig[i] )
lh[i]=dis[idx][i];
}
min=INF;
for(i=1;i<=n;i++)
{
if( !sig[i] && lh[i]<min )
{
min=lh[i];
idx=i;
}
}
sig[idx]=1;
len+=lh[idx];
cnt++;
}
return len;
}

int main()
{

cprim tp;
tp.input();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: