将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。
#include
#include
#include
#include
using namespace std;
struct node{
int x,y;//树一条边的起点和终点;
int w;// 边权(边的价值或大小)
}a[110];
int pre[110];//每个点的信息; 并查集所用;
bool cmp(struct node a,struct node b)
{
return a.w<b.w;
}
int find(int root)// 查找函数(并查集)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
int main()
{
int n,i,m;
while(scanf("%d %d",&n,&m),n)
{
memset(pre,0,sizeof(pre));
int sum=0,num=0;
for(i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);//输入各个边的起点、终点和权值;
sort(a+1,a+1+m,cmp);// 对各个边进行排序;
for(i=1;i<=n;i++) pre[i]=i;
for(i=1;i<=m;i++)// 从小到大开始枚举测试边;用并查集判断是否构成环; 不构成则加入;
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
pre[f1]=f2;
sum+=a[i].w;//存储最小的边权和;
num++;
}
if(num==n-1) break;// 满足树的性质; 边的条数==点的个数-1;
}
if(num==n-1)//判断是否存在;
printf("%d\n",sum);
else printf("?\n");
}
return 0;
}
a、二维数组或结构体或者邻接表存储边权信息;
b、对边进行排序;
c、从小到大枚举测试每条边;
d、判断是否构成最小生成树;
传送门
分析:裸题-----直接套模板
代码:
#include
#include
#include
#include
using namespace std;
struct node{
int x,y,w;
}a[110];
int pre[110];
int find(int root)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
bool cmp(node a,node b)
{
return a.w<b.w;
}
int main()
{
int n,m;
while(scanf("%d%d",&m,&n),m)
{
int sum=0,num=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++) pre[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++)
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
sum+=a[i].w;
pre[f1]=f2;
num++;
}
}
if(num==n-1) printf("%d\n",sum);
else printf("?\n");
}
return 0;
}
传送门
分析:裸题----套模板,但是在数据存储时要严格按照题目要求来,有点麻烦,不直接;
代码:
#include
#include
#include
using namespace std;
struct node{
int x,y,w;
}a[401];
int pre[401];
int find(int root)
{
if(root!=pre[root]) pre[root]=find(pre[root]);
return pre[root];
}
bool cmp(node a,node b)
{
return a.w<b.w;
}
int main()
{
int n;
while(scanf("%d",&n),n)
{
int cnt=0;
for(int i=0;i<n-1;i++)
{
char b; int num;
cin>>b>>num;
for(int j=1;j<=num;j++)
{
char c;int index;
cin>>c>>index;
a[cnt].x=b-'A'+1; a[cnt].y=c-'A'+1; a[cnt++].w=index;
}
}
for(int i=1;i<=n;i++) pre[i]=i;
sort(a,a+cnt,cmp);
int sum=0;
for(int i=0;i<cnt;i++)
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
sum+=a[i].w;
pre[f1]=f2;
}
}
printf("%d\n",sum);
}
return 0;
}
传送门
分析:裸题-----直接套模板
#include
#include
#include
#include
using namespace std;
struct node{
int x,y;
int w;
};
struct node a[5500];
int pre[5500];
bool cmp(struct node a,struct node b)
{
return a.w<b.w;
}
int find(int root)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
int main()
{
int n,i,m;
while(scanf("%d",&n),n)
{
int sum=0,num=0;
scanf("%d",&m);
for(i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
sort(a+1,a+1+m,cmp);
memset(pre,0,sizeof(pre));
for(i=1;i<=n;i++) pre[i]=i;
for(i=1;i<=m;i++)
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
pre[f1]=f2;
sum+=a[i].w;
num++;
}
if(num==m-1) break;
}
printf("%d\n",sum);
}
return 0;
}
传送门
分析:模板题;
#include
#include
#include
using namespace std;
typedef long long ll;
struct node{
int x,y;
ll w;
}a[200010];
int n,m,pre[5001];
int find(int root)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
bool cmp(node a,node b)
{
return a.w<b.w;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) pre[i]=i;
for(int i=1;i<=m;i++) scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].w);
sort(a+1,a+m+1,cmp);
ll sum=0;
int num=0;
for(int i=1;i<=m;i++)
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
pre[f1]=f2;
sum+=a[i].w;
num++;
}
if(num==n-1) break;
}
if(num!=n-1) printf("orz");
else printf("%lld",sum);
return 0;
}
传送门
题意:给出一些村庄,这些村庄直接已经建造了或没建造道路来两两相连,但道路并不全,不能使村庄相互连通,我们要做的是确定要再建造的路,并输出每条路的起点和终点;
分析:最小生成树,我们知道每个村庄的坐标(x,y);那么任意两个村庄的距离就可以得到了。我们可以算出每个村庄到其他村庄的距离并保存。运用Kruskal 算法将这些路排序枚举挑选即可。但要将我们已经建造的道路提前加入集合中。
代码:
#include
#include
#include
#include
using namespace std;
struct node{
int x,y,w;
}a[507*507];// 村庄有n个,则道路最多有n*n条;将范围开到足够;
int pre[507];
int e[507][507];
int find(int root)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
bool cmp(struct node a, struct node b)
{
return a.w<b.w;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,ma=-1,num=0,i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) pre[i]=i;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
int w;
scanf("%d",&w);
e[i][j]=w;
}
}
int k=1;
for(i=1;i<=n;i++)
{
for(j=1;j<i;j++)
{
a[k].x=i;
a[k].y=j;
a[k].w=e[i][j];
k++;
}
}//将已经建造好的道路提前加入集合;
sort(a+1,a+k+1,cmp);
for(i=1;i<k;i++)
{
int f1=find(a[i].x);
int f2=find(a[i].y);
if(f1!=f2)
{
ma=max(ma,a[i].w);
num++;
pre[f1]=f2;
}
if(num==n-1) break;
}
printf("%d\n",ma);
}
return 0;
}
传送门
题意:给出n个长度为7的字符串,每个字符串代表一个车,定义车的距离是两个字符串间不同字母的个数,题目要求的数不同的车的距离的最小值,即所求的就是最小生成树。
分析:套prim模板。只是注意开始先求出距离(即相同位置的不同字母的个数)。
prime和Kruskal差不多,直接看代码就好;
#include
#include
#include
#include
using namespace std;
int e[2010][2010];
int dis[2010],book[2010],inf=0x3f3f3f3f;
char s[2010][11];
int main()
{
int n;
while(scanf("%d",&n),n)
{
for(int i=1;i<=n;i++)
scanf("%s",s[i]);
int cnt=0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
int xx=0;
for(int k=0;k<7;k++)
if(s[i][k]!=s[j][k]) xx++;
e[i][j]=e[j][i]=xx;
}
}
memset(book,0,sizeof(book));
int ans=0;
for(int i=1;i<=n;i++) dis[i]=e[1][i];
book[1]=1;
for(int i=1;i<n;i++)
{
int mi=inf,u;
for(int j=1;j<=n;j++)
{
if(mi>dis[j]&&book[j]==0)
{
mi=dis[j];
u=j;
}
}
book[u]=1;
ans+=dis[u];
for(int j=1;j<=n;j++) dis[j]=min(dis[j],e[u][j]);
}
printf("The highest possible quality is 1/%d.\n",ans);
}
return 0;
}
传送门
分析:与 5、Highways–POJ - 1751 类似;
#include
#include
#include
#include
using namespace std;
struct node{
int x,y;
}a[1001];
struct mp{
int x,y;
double z;
}e[10010];
int pre[10010];
bool cmp(mp a,mp b)
{
return a.z<b.z;
}
double jili(int i,int j)
{
return (a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)*1.0;
}
int find(int root)
{
if(root!=pre[root]) root=find(pre[root]);
return pre[root];
}
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<=n;i++) pre[i]=i;
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
int cnt=1;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
e[cnt].x=i;
e[cnt].y=j;
e[cnt++].z=sqrt(jili(i,j));
}
}
cnt--;
sort(e+1,e+cnt+1,cmp);
double sum=0;
int num=0;
for(int i=1;i<=cnt;i++)
{
int f1=find(e[i].x);
int f2=find(e[i].y);
if(f1!=f2)
{
if(e[i].z>=10&&e[i].z<=1000)
{
num++;
sum+=e[i].z;
pre[f1]=f2;
}
}
if(num==n-1) break;
}
if(num==n-1) printf("%.1f\n",sum*100);
else printf("oh!\n");
}
return 0;
}
最小生成树的题目大多比较固定,就是直接或者间接的套模板,但要注意数据的存储和应用。