先说求无向图最小生成树的两种算法:
①Prim算法
1.初始时两个集合V,E,初始时V为空,E是所有点的集合。
2.以图中任意一个点为起始点,把这点加入集合V,然后从集合E中删除,然后更新这个点到其它点的距离,如果更新不到的点,那么之间的距离为无穷大。
3.然后挑选离起始点最近的点,把这点加入集合V,然后从集合E中删除(有多个的话任选一个),然后更新 新加入的点 到其它点的距离,然后再找出一个距离这个集合最近的点(这个距离最近的点可能离起始点最近)。
4.然后重复上面的步骤,直至路被更新完或E为空,如果E不为空,说明图不连通。
②克鲁斯卡尔算法
1.将两点之间的距离排序
2.每次从中选出距离最短的两点,判断这两点所在的集合是否联通。
3.如果联通,继续步骤2;如果不连通,将两点合并为一个集合,继续步骤2
4.直至所有点都联通,或路径判断完毕
其中稠密图适合用prim,稀疏图适合用克鲁斯卡尔。
两个算法的基础都是贪心算法,下面的水题,练练手。。。
Hdu 1102 Constructing Roads
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1102
题目大意:给你一个矩阵map,其中map[i][j]表示从i到j的花费,给你多次询问,输出询问的两点之间联通的最小花费
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Maxint 0x3f3f3f3f
#define N 110
int map[N][N],visit[N],low[N];
int n,m;
int prim()
{
int Min,sum=0,pos,i,j;
memset(visit,0,sizeof(visit));
visit[1]=1;pos=1;
for(int i=1;i<=n;i++)
if(i!=pos)
low[i]=map[pos][i];
for(int i=1;i<n;i++)
{
Min=Maxint;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]<Min)
{
Min=low[j];
pos=j;
}
sum+=Min;
visit[pos]=1;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return sum;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{scanf("%d",&map[i][j]);map[j][i]=map[i][j];}
scanf("%d",&m);
int a,b;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
map[a][b]=map[b][a]=0;
}
printf("%d\n",prim());
}
return 0;
}
Hdu 1863 畅通工程
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1863
题目大意:中文题,不解释
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Maxint 0x3f3f3f3f
#define N 110
int n,m,rank,map[N][N],sum,f[N];
//克鲁斯卡尔
struct point{
int x,y,dis;
point():x(0),y(0),dis(0){}//结构体的初始化
}s[N];
bool cmp(point a,point b)
{
return a.dis<b.dis;
}
void init()
{
sum=0;
rank=m;
for(int i=0;i<=N;i++)
f[i]=i;
}
int find(int x)
{
if(x!=f[x]) f[x]=find(f[x]);
return f[x];
}
int merge(point a)
{
int fx=find(a.x);
int fy=find(a.y);
if(fx!=fy)
{
f[fx]=fy;
sum+=a.dis;
rank--;
}
return rank;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n)
{
init();
for(int i=0; i<n; i++)
scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].dis);
sort(s,s+n,cmp);
int i;
for(i=0;i<n;i++)
{
if(merge(s[i])==1)
{printf("%d\n",sum);break;}
}
if(rank>1)
printf("?\n");
}
return 0;
}
Hdu 1875 畅通工程再续
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1875
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define Maxint 0x3f3f3f3f
#define N 110
//prim
int visit[N],n;
double low[N],map[N][N];
struct point{
int x;
int y;
}s[N];
void init()//初始化
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
if(i==j) map[i][j]=0;
else map[i][j]=Maxint;
}
}
double prim()
{
double Min,sum=0;
int pos,i,j;
memset(visit,0,sizeof(visit));
visit[0]=1;pos=0;
for(int i=0;i<n;i++)
if(i!=pos)
low[i]=map[pos][i];
for(int i=1;i<n;i++)
{
Min=Maxint;
for(int j=0;j<n;j++)
if(visit[j]==0&&low[j]<Min)
{
Min=low[j];
pos=j;
}
if(Min==Maxint) return -1;
sum+=Min;
visit[pos]=1;
for(int j=0;j<n;j++)
if(visit[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return sum;
}
double S(point a,point b)//两坐标之间距离
{
double len;
len=sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
return len;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(int i=0;i<n;i++)
scanf("%d%d",&s[i].x,&s[i].y);
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
double len=S(s[i],s[j]);
if(len>=10&&len<=1000)
map[i][j]=map[j][i]=len*100;
}
double ans=prim();
if(ans==-1) printf("oh!\n");
else printf("%.1lf\n",ans);
}
return 0;
}
Hdu 1879 继续畅通工程
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1879
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define Maxint 0x3f3f3f3f
#define N 110
//prim
struct point{
int x,y,dis,f;
}s[100005]; //注意s的范围
int visit[N],n,low[N],map[N][N];
void init()
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
if(i==j) map[i][j]=0;
else map[i][j]=Maxint;
}
}
int prim()
{
int Min,sum=0;
int pos,i,j;
memset(visit,0,sizeof(visit));
visit[1]=1;pos=1;
for(int i=1;i<=n;i++)
if(i!=pos)
low[i]=map[pos][i];
for(int i=1;i<n;i++)
{
Min=Maxint;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]<Min)
{
Min=low[j];
pos=j;
}
sum+=Min;
visit[pos]=1;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return sum;
}
int main()
{
while(scanf("%d",&n)&&n)
{
int a=n*(n-1)/2;
init();
for(int i=0; i<a; i++)
{
scanf("%d%d%d%d",&s[i].x,&s[i].y,&s[i].dis,&s[i].f);
if(s[i].f==1) map[s[i].x][s[i].y]=map[s[i].y][s[i].x]=0;
else map[s[i].x][s[i].y]=map[s[i].y][s[i].x]=s[i].dis;
}
printf("%d\n",prim());
}
return 0;
}
Hdu 3371 Connect the Cities
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3371
题目大意:有N个城市,M条路,并且在输完路之后,再输入K个城市群,每个城市群里有P个城市相连,这P个城市之间的花费为0,求所有城市相连的最小花费,不能全部相连输出-1.
注意:代码用G++超时,用C++ 500ms AC
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define Maxint 0x3f3f3f3f
#define N 510
int visit[N],n,low[N],map[N][N],v[N];
void init()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) map[i][j]=0;
else map[i][j]=map[j][i]=Maxint;
}
}
int prim()
{
int Min,sum=0;
int pos,i,j;
memset(visit,0,sizeof(visit));
visit[1]=1;pos=1;
for(int i=1;i<=n;i++)
if(i!=pos)
low[i]=map[pos][i];
for(int i=1;i<n;i++)
{
Min=Maxint;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]<Min)
{
Min=low[j];
pos=j;
}
if(Min==Maxint) return -1;
sum+=Min;
visit[pos]=1;
for(int j=1;j<=n;j++)
if(visit[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return sum;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int m,k,num;
scanf("%d%d%d",&n,&m,&k);
init();
int a,b,c;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c) map[a][b]=c,map[b][a]=c;
}
for(int i=1;i<=k;i++)
{
scanf("%d",&num);
for(int j=0;j<num;j++)
scanf("%d",&v[j]);
for(int j=0;j<num;j++)
for(int kk=j+1;kk<num;kk++)
map[v[kk]][v[j]]=map[v[j]][v[kk]]=0;
}
printf("%d\n",prim());
}
return 0;
}
Hdu 3367 Pseudoforest 最大生成树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3367
题目大意:将所有的点相连的最大花费,但相连的图中最多只有一个环。
代码如下:
//克鲁斯卡尔
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int f[N],visit[N];
int sum;
struct point{
int x,y,dis;
}s[100005];
bool cmp(point a,point b)
{
return a.dis>b.dis;
}
void init()
{
sum=0;
memset(visit,0,sizeof(visit));
for(int i=0;i<N;i++)
f[i]=i;
}
int find(int x)
{
if(x!=f[x]) f[x]=find(f[x]);
return f[x];
}
void merge(point a)
{
int fx=find(a.x);
int fy=find(a.y);
if(fx==fy&&visit[fx]==0)
{
sum+=a.dis;
visit[fx]=1;
}
else
{
if(visit[fx]==0&&visit[fy]==0)
{
f[fx]=fy;
sum+=a.dis;
}
else if(visit[fx]==0||visit[fy]==0)
{
visit[fx]=1;
visit[fy]=1;
f[fx]=fy;
sum+=a.dis;
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)&&n+m)
{
init();
for(int i=0;i<m;i++)
scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].dis);
sort(s,s+m,cmp);
for(int i=0;i<m;i++)
merge(s[i]);
printf("%d\n",sum);
}
return 0;
}
关于有向图的最小生成树,应该是最小树形图,会在接下来整理。。。