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

所以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;
}

 

 

 

你可能感兴趣的:(c,算法,Class,input,output)