kuangbin专题之最小生成树
这个专题比我想象中的要简单一些…关于次小生成树先放一放…只会一个暴力的写法----------------------------------
开头先放一下我自己习惯的写法吧,众所周知,最小生成树有两种算法kruskal和prime算法
kruskal算法
适用于点多边少的图(稀疏图)
这个算法简直是太好理解且容易写了…就是一个贪心+并查集的思想
有一说一我直接看明白了…算是我目前学的算法中学的最快的一个…
大概就是读入的时候记录下每条边的出发点,终点,权值
用一个并查集来维护每个点是否已经在同一个生成树里面了
我们先sort一下把权值小的尽量放前面,这样我们合并点的时候就是尽可能地找权值小的边 我们每次找的两个点判断根节点是不是一样,一样的话就代表这俩点已经联通,跳过就好,没联通的话,那目前就是最小的权值可以使它们联通所以直接连通,每次加入一个点就cnt++ 最后看cnt是否与总的点数n是否满足cnt==n-1满足的话就代表成功构成树了
自己的板子
struct node{
int fr,to,val;
}arr[maxn];
int dad[maxn];
int seek(int x){
if(x==dad[x]){
return x;
}else{
return dad[x]=seek(dad[x]);
}
}
bool cmp(node a,node b){
if(a.val==b.val){
return a.fr<b.fr;
}else{
return a.val<b.val;
}
}
void kruskal(int n,int m){
rep(i,1,n){
dad[i]=i;
}
int ans=0,cnt=0;
rep(i,1,m){
int a=arr[i].fr,b=arr[i].to,c=arr[i].val;
int fa=seek(a),fb=seek(b);
if(fa!=fb){
dad[fa]=fb;
cnt++;
ans+=c;
}
}
if(cnt==n-1){///判断是否成功搭建
cout<<ans<<endl;
}else{
cout<<"impossible"<<endl;
}
}
void solve(){///读入边
int n=read(),m=read();
rep(i,1,m){
arr[i].fr=read();
arr[i].to=read();
arr[i].val=read();
}
sort(arr+1,arr+m+1,cmp);
kruskal(n,m);
}
prime算法
适用于点少边多的图(稠密图)
这个算法的思想是类似于最短路吧,不断松弛每个点到树里面每个点的距离
每次找出一个目前距离树里面点最近的点,然后把这个点加入树中并继续松弛
int e[maxn][maxn],dis[maxn],vis[maxn];
void prime(int st,int n){
rep(i,1,n){
dis[i]=e[st][i];
vis[i]=0;
}
vis[st]=1;
int ans=0,cnt=0;while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
cnt++;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
if(cnt==n-1){
cout<<ans<<endl;
}else{
cout<<"impossible"<<endl;
}
}
void solve(){
int n=read(),m=read();
rep(i,1,n){
e[i][i]=0;
rep(j,i+1,n){
e[i][j]=e[j][i]=inf;
}
}
while(m--){
int a=read(),b=read(),c=read();
e[a][b]=e[b][a]=min(e[a][b],c);
}
prime(1,n);
}
------------------------------------------------------------------------------------------------------------------------
题目地址
A - Jungle Roads POJ - 1251
最小生成树模板题
给出了不同点之间的距离,用字母代替的,求出连通起来最小花费
prime写法
int e[27][27];
int vis[27],dis[27];
int prime(int st,int n){
ms(vis,0);vis[1]=1;
rep(i,1,n){
dis[i]=e[st][i];
}
int ans=0;
while(true){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve()
{
int n;while(cin>>n){
if(!n)break;
rep(i,1,n){
rep(j,1,n){
if(i==j)e[i][j]=0;
else{
e[i][j]=inf;
}
}
}
rep(i,1,n-1){
char ch;
int num;
cin>>ch>>num;
while(num--){
char ed;
int len;
cin>>ed>>len;
int a=ch-'A'+1,b=ed-'A'+1;
e[a][b]=e[b][a]=min(e[a][b],len);
}
}
cout<<prime(1,n)<<endl;
}
}
kruskal写法
struct node
{
int fr,to,val;
}arr[maxn];
int dad[maxn];
int seek(int x){
if(x==dad[x])
return x;
return dad[x]=seek(dad[x]);
}
void mix(int a,int b){
int fa=seek(a),fb=seek(b);
dad[fa]=fb;
}
bool cmp(node a,node b){
if(a.val==b.val)
return a.fr<b.fr;
else
return a.val<b.val;
}
int kruskal(int n){
int ans=0;
for(int i=1;i<=n;i++){
int a=arr[i].fr,b=arr[i].to;
int fa=seek(a),fb=seek(b);
if(fa==fb)continue;
ans+=arr[i].val;
mix(a,b);
}
return ans;
}
void solve()
{
int n;
while(cin>>n){
if(!n)break;
for(int i=1;i<=27;i++){
dad[i]=i;
}
int num=0;
rep(i,1,n-1){
char st,ed;
int cnt=0,val;
cin>>st>>cnt;
while(cnt--){
cin>>ed>>val;
arr[++num].fr=st-'A'+1;
arr[num].to=ed-'A'+1;
arr[num].val=val;
}
}
sort(arr+1,arr+num+1,cmp);
cout<<kruskal(num)<<endl;
}
}
B - Networking POJ - 1287
也是模板题,给出点的数目n和边的数目m 然后m条边的信息
只不过这一个没有限制边的数目,可以无穷大,所以我们只能用prime算法来写了
int e[maxn][maxn];
int vis[maxn],dis[maxn];
int prime(int st,int n){
ms(vis,0);vis[st]=1;
rep(i,1,n){
dis[i]=e[st][i];
}
int ans=0;
while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve()
{
int n,m;
while(~scanf("%d",&n)){
if(!n)break;
rep(i,1,n){
rep(j,1,n){
if(i==j)e[i][j]=0;
else e[i][j]=inf;
}
}
int m;scanf("%d",&m);
while(m--)
{
int a,b,c;scanf("%d %d %d",&a,&b,&c);
e[a][b]=e[b][a]=min(e[a][b],c);
}
printf("%d\n",prime(1,n));
}
}
C - Building a Space Station POJ - 2031
也是一道简单题,只不过这个题没有给出边的信息,只给出了每个点的坐标以及半径,我们需要人为的去处理下信息来获得每个点之间距离的邻接矩阵
两点间距离小于等于r1+r2的都视为0,大于的就等于距离-r1-r2就好了
很明显这道题用prime会更好一些
struct node{
double x,y,z,r;
}arr[maxn];
double dis[maxn],e[maxn][maxn];
int vis[maxn];
double get(node a,node b){
double zz=sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2)+pow(a.z-b.z,2));
if(zz<=a.r+b.r){
return 0.0;
}else{
return 1.0*(zz-a.r-b.r);
}
}
void init(int n){
rep(i,1,n){
rep(j,1,n){
if(i!=j){
double tmp=get(arr[i],arr[j]);
e[i][j]=tmp;
}
}
}
}
double prime(int st,int n){
double ans=0;
ms(vis,0),vis[st]=1;
rep(i,1,n){
dis[i]=e[st][i];
}
while(1){
double mn=inf*1.0;
int pos=-1;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve(){
int n;while(~scanf("%d",&n)&&n){
rep(i,1,n){
rep(j,1,n){
if(i==j)e[i][j]=0.0;
else{
e[i][j]=inf*1.0;
}
}
}
rep(i,1,n){
scanf("%lf %lf %lf %lf",&arr[i].x,&arr[i].y,&arr[i].z,&arr[i].r);
}
init(n);
printf("%.3f\n",prime(1,n));
}
}
D - Constructing Roads POJ - 2421
也是一道简单题,n个点,然后给出nxn矩阵来代表点和点之间的关系,区别在于,有些地方之间已经提前联通好了,求需要新连接的最小值
我们只需要输入矩阵后,再把已经连接好的两个点 之间权值改为0就可以了
int e[maxn][maxn],dis[maxn],vis[maxn];
int prime(int st,int n){
rep(i,1,n){
vis[i]=0;
rep(j,1,n){
dis[i]=e[st][i];
}
}
vis[st]=1,dis[st]=0;
int ans=0;
while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
pos=i;
mn=dis[i];
}
}
if(pos==-1){break;}
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve(){
int n=read();
rep(i,1,n){
rep(j,1,n){
if(i==j)e[i][j]=0;
else {
e[i][j]=inf;
}
}
}
rep(i,1,n){
rep(j,1,n){
int tmp=read();
e[i][j]=e[j][i]=min(tmp,e[i][j]);
}
}
int q=read();while(q--){
int a=read(),b=read();
e[a][b]=e[b][a]=0;
}
printf("%d\n",prime(1,n));
}
E - QS Network ZOJ - 1586
t组样例 n个点
然后是每个点的价值,
之后给出一个矩阵代表点和点之间的距离,两个点之间的权值为两点的价值与距离之和,所以我们处理一下矩阵 每个权值都加上两点的价值就好了
int e[maxn][maxn],dis[maxn],vis[maxn],val[maxn];
int prime(int st,int n){
rep(i,1,n){
vis[i]=0;
rep(j,1,n){
dis[i]=e[st][i];
}
}
vis[st]=1,dis[st]=0;
int ans=0;
while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
pos=i;
mn=dis[i];
}
}
if(pos==-1){break;}
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve(){
int n=read();
rep(i,1,n){
val[i]=read();
rep(j,1,n){
if(i==j)e[i][j]=0;
else {
e[i][j]=inf;
}
}
}
rep(i,1,n){
rep(j,1,n){
int tmp=read();
tmp+=val[i]+val[j];
e[i][j]=tmp;
}
}
printf("%d\n",prime(1,n));
}
F - Truck History POJ - 1789
。。。我发现这个专题的难点都是变着法的让你处理下点与点之间距离…
这道题是给出数目n 然后n个字符串,每两个点之间的距离等于这俩点为下标的字符串中每个位置不同字符的个数…处理完距离就跑个模板就好了
int dis[maxn],e[maxn][maxn],vis[maxn];
char str[maxn][maxn];
int get(int i,int j){
int num=0;
rep(k,1,7){
num+=str[i][k]!=str[j][k];
}
return num;
}
int prime(int st,int n){
rep(i,1,n){
vis[i]=0;
dis[i]=e[st][i];
}
vis[st]=1,dis[st]=0;
int ans=0;while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve()
{
int n;while(~scanf("%d",&n)&&n){
getchar();
rep(i,1,n){
scanf("%s",str[i]+1);
}
rep(i,1,n){
e[i][i]=0;
rep(j,1,i-1){
e[i][j]=e[j][i]=get(i,j);
}
}
printf("The highest possible quality is 1/%d.\n",prime(1,n));
}
}
G - Arctic Network POJ - 2349
新鲜题
给出m,n 和n个点的坐标 你可以无视m个点使得这m个点之间零消耗(m-1条边)
其余的联通需要花费,求其中最小花费的的最大值
我们处理完距离之后得到的dis数组就是到每个点的权值最小值,然后我们sort一遍,取dis[n-(m-1)]即dis[n-m+1]
其实这个题我想了好久 因为m为什么可以抵消m-1条边 比如m为3 假设可以抵消m-1就是2条 但是如果这两条边两个端点都不一样。那就是4个点? 那怎么满足,我自己打印样例打了半天才明白…这最大的m-1条边我们是按照最小的情况算出来的。但是我们可以抵消的话,那就没有必要要求最小了
假设点的分布为
0 3 4 7 8 n为5 m为3
那么我们排序之后就是
dis[1]= 0–>0 0
dis[2]= 3–>4 1
dis[3]= 7–>8 1
dis[4]= 0–>3 3
dis[5]= 4–>7 3
那么按照答案来说就是 dis[n-m+1]=dis[3]=1
也就是说3个卫星连了两条最大边 3 3
但我们发现 最大的两条边3 3 端点有四个?0 3 4 7
难道说这个算法错了?
不不不,其实是我们连接边的方式错了,既然最大边可以抵消,那我们就可以使它更大,这样也不会影响结果,既然可以更大,那么就可以改变端点了从而使得可以满足条件
我们把最大的那几个边修改为
dis[4]= 0–>3 3 不变 dis[5]= 4–> 7 3改成 dis[5]= 0–>7 7这样一来就只有三个端点了
所以我们m个卫星删掉m-1条边的时候 是一个放在起点,其他的m-1个放在m-1个最大边的结尾处!就OK了,那么就放代码吧
double e[maxn][maxn],dis[maxn];
int vis[maxn];
pair<double,double>p[maxn];
double get(int x,int y){
return sqrt(pow(p[x].fi-p[y].fi,2)+pow(p[x].se-p[y].se,2));
}
void prime(int st,int n){
rep(i,1,n){
vis[i]=0;
dis[i]=e[st][i];
}
vis[st]=1,dis[st]=0.0;
while(1){
int pos=-1;
double mn=inf*1.0;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
}
void solve()
{
int m=read(),n=read();
rep(i,1,n){
scanf("%lf %lf",&p[i].fi,&p[i].se);
}
rep(i,1,n){
rep(j,1,n){
e[i][j]=get(i,j);
}
}
prime(1,n);
sort(dis+1,dis+n+1);/*
rep(i,1,n){
de(dis[i]);
}*/
printf("%.2f\n",dis[n-m+1]);
}
H - Highways POJ - 1751
这题巨坑!!!
我交C++无限超时。。。改成G++就过了(我特么。。。)
也是一个简单题,唯一不同的是需要输出路径(但对路径顺序没有要求)
那就很简单了,用kruskal算法加边的时候,每加上一条边就存下来,最后输出就好了
double xx[maxn],yy[maxn];
struct node{
int fr,to;
double val;
}edge[maxn*maxn/2];
int num;
int dad[maxn];
int seek(int x){
if(x==dad[x]){
return x;
}else{
return dad[x]=seek(dad[x]);
}
}
double get(int a,int b){
return pow(xx[a]-xx[b],2)+pow(yy[a]-yy[b],2);
}
void add(int a,int b,double c){
++num;
edge[num].fr=a;
edge[num].to=b;
edge[num].val=c;
}
bool cmp(node a,node b){
if(a.val==b.val){
return a.fr>b.fr;
}else{
return a.val<b.val;
}
}
void kruskal(int n,int m){
int cnt=0;
rep(i,1,m){
int a=edge[i].fr,b=edge[i].to;
int fa=seek(a),fb=seek(b);
if(fa!=fb){
dad[fa]=fb;
cnt++;
xx[cnt]=a;
yy[cnt]=b;
}
}
rep(i,1,cnt){
printf("%.0f %.0f\n",xx[i],yy[i]);
}
}
void solve()
{
int n=read();rep(i,1,n){
scanf("%lf %lf",&xx[i],&yy[i]);
dad[i]=i;
}
num=0;
rep(i,1,n){
rep(j,i+1,n){
add(i,j,get(i,j));
}
}
int m=read();while(m--){
int a=read(),b=read();
int fa=seek(a),fb=seek(b);
dad[fa]=fb;
}
sort(edge+1,edge+num+1,cmp);
kruskal(n,num);
}
I - Agri-Net POJ - 1258
简单题,给出一个邻接矩阵求最小生成树,直接prime就好了
int e[maxn][maxn];
int dis[maxn],vis[maxn];
int prime(int st,int n){
rep(i,1,n){
vis[i]=0;
dis[i]=e[st][i];
}
vis[st]=1;
int ans=0;
while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve()
{
int n;while(~scanf("%d",&n)){
rep(i,1,n){
rep(j,1,n){
e[i][j]=read();
}
}
printf("%d\n",prime(1,n));
}
}
J - Borg Maze POJ - 3026
最小生成树加BFS
仔细读题能发现这个本质是最小生成树,但问题是任意两点间的距离怎么求,首先我们发现S和A没有本质的区别,直接一视同仁,然后我们记录下每一个目的点的位置,然后编号,然后遍历,每次找到一个编号就bfs搜出到其它点的距离就OK了
int n,m,all;
char str[105][105];
int pos[105][105];
int vis[105][105];
int used[105];
int e[105][105],dis[105];
int nx[4]={0,1,0,-1};
int ny[4]={1,0,-1,0};
struct nd{
int x,y,s;
};
void bfs(int sx,int sy){
ms(vis,0);
vis[sx][sy]=1;
queue<nd>que;
nd now={sx,sy,0};que.push(now);while(!que.empty()){
now=que.front(),que.pop();
rep(i,0,3){
int tx=now.x+nx[i],ty=now.y+ny[i],s=now.s+1;
if(tx<1||tx>n||ty<1||ty>m||vis[tx][ty]||str[tx][ty]=='#'){
continue;
}
vis[tx][ty]=1;
nd nxt={tx,ty,s};
que.push(nxt);
if(pos[tx][ty]){
int a=pos[sx][sy],b=pos[tx][ty];
e[a][b]=s;
}
}
}
}
void prime(){
rep(i,1,all){
dis[i]=e[1][i];
used[i]=0;
}
used[1]=1;dis[1]=0;
int ans=0;while(1){
int k=-1,mn=inf;
rep(i,1,all){
if(!used[i]&&dis[i]<mn){
mn=dis[i];
k=i;
}
}
if(k==-1)break;
ans+=mn;
//de(mn);//de(k);
used[k]=1;rep(j,1,all){
if(!used[j]&&dis[j]>e[k][j]){
dis[j]=e[k][j];
}
}
}
printf("%d\n",ans);
}
void solve()
{
all=0,ms(pos,0);
scanf("%d %d",&m,&n);
gets(str[0]);
rep(i,1,n){
gets(str[i]+1);
rep(j,1,m){
if(str[i][j]=='A'||str[i][j]=='S'){
pos[i][j]=++all;
}
}
}
ms(e,0);
rep(i,1,n){
rep(j,1,m){
if(pos[i][j]){
bfs(i,j);
}
}
}/*
rep(i,1,all){
de(e[1][i]);
}*/
prime();
}
K - The Unique MST POJ - 1679
判断最小生成树是否唯一
搜的博客说是次小生成树?但我还没学…不过学会了一个暴力查找是不是唯一的方法,直接记录下生成树时的每条边,然后依次屏蔽每条边,假设某条边被屏蔽后还能构成最小生成树且权值一样,就代表不唯一
struct node{
int fr,to,val;
}edge[maxm];
int path[maxm];
bool cmp(node a,node b){
if(a.val==b.val)return a.fr>b.fr;
else{
return a.val<b.val;
}
}
int dad[maxn];
int seek(int x){
if(x==dad[x])return x;
else{
return dad[x]=seek(dad[x]);
}
}
void mix(int a,int b){
int fa=seek(a),fb=seek(b);
dad[fa]=fb;
}
int kruskal(int n,int m){
int ans=0,cnt=0;
rep(i,1,m){
int a=edge[i].fr,b=edge[i].to,c=edge[i].val;
int fa=seek(a),fb=seek(b);
if(fa==fb)continue;
path[++cnt]=i;
ans+=c;
mix(a,b);
}
rep(j,1,cnt){
int sum=0,k=0;
rep(i,1,n){
dad[i]=i;
}
rep(i,1,m){
if(i!=path[j]){
int fa=seek(edge[i].fr),fb=seek(edge[i].to);
if(fa!=fb){
k++;
sum+=edge[i].val;
dad[fa]=fb;
}
}
}
if(sum==ans&&k==n-1){
return -1;
}
}
return ans;
}
void solve()
{
int n=read(),m=read();
rep(i,1,n){dad[i]=i;}
rep(i,1,m){
edge[i].fr=read(),edge[i].to=read(),edge[i].val=read();
}
sort(edge+1,edge+m+1,cmp);
int ans=kruskal(n,m);
if(ans==-1){
puts("Not Unique!");
}else{
printf("%d\n",ans);
}
}
L - 还是畅通工程 HDU - 1233
。。板子题。。直接根据题意构建邻接矩阵就好了
int e[maxn][maxn];
int vis[maxn],dis[maxn];
int prime(int st,int n){
ms(vis,0);vis[st]=1;
rep(i,1,n){
dis[i]=e[st][i];
}
int ans=0;
while(1){
int pos=-1,mn=inf;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
mn=dis[i];
pos=i;
}
}
if(pos==-1)break;
vis[pos]=1;
ans+=mn;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
return ans;
}
void solve()
{
int n,m;
while(~scanf("%d",&n)){
if(!n)break;
rep(i,1,n){
rep(j,1,n){
if(i==j)e[i][j]=0;
else e[i][j]=inf;
}
}
int m=n*(n-1)/2;
while(m--)
{
int a,b,c;scanf("%d %d %d",&a,&b,&c);
e[a][b]=e[b][a]=min(e[a][b],c);
}
printf("%d\n",prime(1,n));
}
}
M - Jungle Roads HDU - 1301
这个。。这个和A题重复了? 我也不清楚什么情况
N - 畅通工程再续 HDU - 1875
判断是否可以构成生成树
对于kruskal算法
我们在加边的时候记录,每次可加就计数,最后不等于n-1就代表无法构成
对于prime算法
每次找最近点,找到就计数,不等于n-1就代表错误,(有没有最近点取决于mn是否小于inf)
struct in{
double x,y;
}cup[maxn];
double dis[maxn];
int vis[maxn];
double e[maxn][maxn];
double get(int a,int b){
double ans=sqrt(pow(cup[a].x-cup[b].x,2)+pow(cup[a].y-cup[b].y,2));
if(ans>=10&&ans<=1000){return ans;}
else{
return inf*1.0;
}
}
double prime(int st,int n){
rep(i,1,n){
vis[i]=0;
dis[i]=e[st][i];
}
vis[st]=1,dis[st]=0;
int num=0;
double ans=0;
while(1){
int pos=-1;
double mn=inf*1.0;
rep(i,1,n){
if(!vis[i]&&dis[i]<mn){
pos=i;
mn=dis[i];
}
}
if(pos==-1){break;}
vis[pos]=1;
ans+=dis[pos];
num++;
rep(i,1,n){
if(!vis[i]&&dis[i]>e[pos][i]){
dis[i]=e[pos][i];
}
}
}
if(num!=n-1)return -1*1.0;
else{
return ans;
}
}
void solve()
{
int n=read();
rep(i,1,n){
scanf("%lf %lf",&cup[i].x,&cup[i].y);
rep(j,1,n){
if(i==j)e[i][j]=0.0;
else{
e[i][j]=inf*1.0;
}
}
}
rep(i,1,n){
rep(j,1,n){
if(i==j)continue;
e[i][j]=get(i,j);
}
}
double ans=prime(1,n);
if(ans==-1.0)puts("oh!");
else{
printf("%.1f\n",ans*100);
}
}
又刷完一个专题 奥里给!!