[kuangbin带你飞]专题六 最小生成树

A. POJ 1251  Jungle Roads

模板题

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

const int INF=(1<<29);
map<char,int> STL;
int edge[30][30];
int mincost[30];
bool used[30];

int n,m,from,to,w;


int main()
{
    for(int i=0;i<26;i++)
        STL[i+'A']=i;
    while(cin>>n&&n)
    {
        char ch;
        int ed;
        for(int i=0;i<30;i++)
            for(int j=0;j<30;j++)
            {
                edge[i][j]=INF;
                used[i]=false;
                mincost[i]=INF;
            }
        for(int i=0;i<n-1;i++)
        {
            cin>>ch>>m;
            for(int j=0;j<m;j++)
            {
                cin>>ch>>ed;
                edge[i][STL[ch]]=edge[STL[ch]][i]=min(edge[i][STL[ch]],ed);
            }
        }
        int res=0;
        mincost[0]=0;
        while(1)
        {
            int v=-1;
            for(int u=0;u<n;u++)
                if(!used[u]&&(mincost[v]>mincost[u]||v==-1))
                    v=u;
            if(v==-1)
                break;
            used[v]=true;
            res+=mincost[v];
            for(int u=0;u<n;u++)
                mincost[u]=min(mincost[u],edge[v][u]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

B. POJ 1287  Networking

模板题

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

#define INF 1000000000

using namespace std;

int edge[55][55];
int d[55];
bool used[55];

int n,m,from,to,w;

int main()
{
    while(scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        for(int i=0;i<55;i++)
            for(int j=0;j<55;j++)
                edge[i][j]=INF;
        for(int i=1;i<55;i++)
        {
            d[i]=INF;
            used[i]=false;
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&from,&to,&w);
            edge[to][from]=edge[from][to]=min(edge[to][from],w);
        }

        d[1]=0;
        int res=0;
        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(v==-1||d[u]<d[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=true;
            res+=d[v];
            for(int u=1;u<=n;u++)
                d[u]=min(d[u],edge[u][v]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

C. POJ 2031  Building a Space Station

题意:有n个空间站,告诉你它们的三维坐标以及半径,你需要把他们都连接起来。

思路:设半径分别为r1,r2,空间站距离为d。如果的d<=r1+r2,那么连一条权值为0的边,如果d>r1+r2就连一条距离为d-r1-r2的边。然后套模板就好。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

double dist[110][110];
double mincost[110];
bool used[110];

struct Point{
    double x,y,z,r;
}point[110];

double fun(int i,int j)
{
    Point a=point[i];
    Point b=point[j];
    double d=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
    return (d>a.r+b.r)?d-a.r-b.r:0;
}
int n;

int main()
{
    while(cin>>n&&n)
    {
        memset(point,0,sizeof(point));
        for(int i=0;i<n;i++)
        {
            used[i]=false;
            mincost[i]=1000.0;
        }
        for(int i=0;i<n;i++)
        {
            cin>>point[i].x>>point[i].y>>point[i].z>>point[i].r;
            for(int j=0;j<i;j++)
                dist[i][j]=dist[j][i]=fun(i,j);
        }

        double res=0;
        mincost[0]=0;
        while(1)
        {
            int v=-1;
            for(int u=0;u<n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            res+=mincost[v];
            for(int u=0;u<n;u++)
                mincost[u]=min(mincost[u],dist[u][v]);
        }
        printf("%.3f\n",res);
    }
    return 0;
}
View Code

D. POJ 2421  Constructing Roads

题意:有n个村庄,Q条修好的路,给你n个村庄的邻接矩阵,问你最小生成树是多少。

思路:在邻接矩阵中把已有的路的两个节点的距离改为0。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

int dist[110][110];
int mincost[110];
bool used[110];

int n,Q;

int main()
{
    while(cin>>n&&n)
    {
        for(int i=0;i<=n;i++)
        {
            used[i]=false;
            mincost[i]=1000000;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            cin>>dist[i][j];
        cin>>Q;
        for(int i=0;i<Q;i++)
        {
            int a,b;
            cin>>a>>b;
            dist[a][b]=dist[b][a]=0;
        }
        int res=0;
        mincost[1]=0;
        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            res+=mincost[v];
            for(int u=1;u<=n;u++)
                mincost[u]=min(mincost[u],dist[u][v]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

E. ZOJ 1586  QS Network

题意:在光缆的价格矩阵上加上这两个节点所喜欢的插头的价格就行。然后直接裸的最小生成树。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

int dist[1010][1010];
int mincost[1010];
bool used[1010];
int T,n;
int main()
{
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&mincost[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&dist[i][j]);
                if(i!=j)
                    dist[i][j]=dist[i][j]+mincost[i]+mincost[j];
            }

        for(int i=1;i<=n;i++)
        {
            used[i]=false;
            mincost[i]=(1<<29);
        }

        int res=0;
        mincost[1]=0;

        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            res+=mincost[v];
            for(int u=1;u<=n;u++)
                mincost[u]=min(mincost[u],dist[v][u]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

F. POJ 1789  Truck History

题意: 每种卡车都有一个编号,一个编号只能由另一个编号变化过来,变化的代价就是两个编号相同位置字母不同的位置的个数。

思路: 其实就是一个完全图,求最小生成树。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

#define INF 10
using namespace std;
char str[2010][10];
int edge[2010][2010];
int mincost[2010];
bool used[2010];

int fun(int i,int j)
{
    int ans=0;
    for(int k=0;k<7;k++)
        if(str[i][k]!=str[j][k])
        ans++;
    return ans;
}

int main()
{
    int n;
    while(scanf("%d",&n)&&n)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%s",str[i]);
            for(int j=0;j<i;j++)
            {
                edge[j][i]=edge[i][j]=fun(i,j);
            }
        }

        for(int i=0;i<n;i++)
        {
            mincost[i]=INF;
            used[i]=false;
        }

        mincost[0]=0;
        int res=0;

        while(1)
        {
            int v=-1;
            for(int u=0;u<n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;

            if(v==-1)
                break;
            used[v]=true;
            res+=mincost[v];

            for(int u=0;u<n;u++)
                mincost[u]=min(mincost[u],edge[v][u]);
        }
        printf("The highest possible quality is 1/%d.\n",res);
    }
    return 0;
}
View Code

G. POJ 2349  Arctic Network

题意:有n个前哨战,每一个前哨站可以跟其他前哨站交流,花费为他们的距离。同时你拥有s个卫星,任意两个有卫星的前哨站可以直接交流,没有花费。问你所需要的最小花费是多少,最小花费为最小生成树中最长的边。

思路:转化题意,实际上就是求最短路中第s+1长的边。可以prim也可以kruskal,实际上kruskal应该更简单。我这里写的是prim。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

double dist[550][550];
double mincost[550];
bool used[550];

struct Point{
    int x,y;
}point[550];

double fun(int i,int j)
{
    return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)*1.0+(point[i].y-point[j].y)*(point[i].y-point[j].y)*1.0);
}
int T,n,m;

int main()
{
    cin>>T;
    while(T--)
    {
        int from,to,w,s,p;
        cin>>s>>p;
        for(int i=0;i<p;i++)
        {
            cin>>point[i].x>>point[i].y;
            for(int j=0;j<i;j++)
                dist[i][j]=dist[j][i]=fun(i,j);
        }
        for(int i=0;i<p;i++)
        {
            used[i]=false;
            mincost[i]=100000000.0;
        }
        mincost[0]=0;
        int cnt=0;
        while(1)
        {
            int v=-1;
            cnt++;
            for(int u=0;u<p;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            for(int u=0;u<p;u++)
                if(v!=u&&!used[u])
                    mincost[u]=min(mincost[u],dist[v][u]);
        }
        sort(mincost,mincost+p);
        printf("%.2f\n",mincost[p-s]);
    }
    return 0;
}
View Code

H. POJ 1751  Highways

题意:有n个塔,现在在塔之间已经有m条高速公路了,你需要加上高速公路,使得他们任意两点间可达,问你需要的花费是多少。

思路:把已有的边的权值设为0,然后求一遍最小生成树就好。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

int T,n,m,cnt;
double dist[800][800];
double mincost[800];
int ans[800];
int par[11000];
int rank[11000];
bool used[800];

struct Point{
    int x,y;
}point[800];

double fun(int i,int j)
{
    return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
}

int main()
{
    while(cin>>n)
    {
        int from,to,w;
        cnt=0;
        memset(dist,0,sizeof(dist));
        memset(used,0,sizeof(used));
        for(int i=0;i<=n;i++)
        {
            mincost[i]=1000000000.0;
            ans[i]=i;
        }
        for(int i=1;i<=n;i++)
        {
            cin>>point[i].x>>point[i].y;
            for(int j=1;j<i;j++)
                dist[j][i]=dist[i][j]=fun(i,j);
        }
        cin>>m;
        for(int i=0;i<m;i++)
        {
            cin>>from>>to;
            dist[from][to]=dist[to][from]=0;
        }
        //sort(ed,ed+m,comp);
        //ans=kruskal(-1);
        int last=1;
        mincost[1]=0;
        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(mincost[u]<mincost[v]||v==-1))
                    v=u;
            if(v==-1)
                break;
            used[v]=true;
            if(mincost[v])
                printf("%d %d\n",ans[v],v);
            for(int u=1;u<=n;u++)
                if(mincost[u]>dist[u][v])
                {
                    mincost[u]=min(mincost[u],dist[u][v]);
                    ans[u]=v;
                }
        }
    }
    return 0;
}
View Code

I. POJ 1258  Agri-Net

题意:输入一个矩阵,求最小生成树。裸题。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

int T,n,m,cnt;
int dist[110][110];
int mincost[110];
int par[11000];
int rank[11000];
bool used[11000];

struct edge{
    int from,to,w;
}ed[11000];

bool comp(edge a,edge b)
{
    return a.w<b.w;
}

void inti(int n)
{
    for(int i=0;i<=n;i++)
    {
        par[i]=i;
        rank[i]=0;
    }
}

int find(int x)
{
    if(par[x]==x)
        return x;
    return par[x]=find(par[x]);
}

void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)
        return;
    if(rank[x]<rank[y])
        par[x]=y;
    else
    {
        par[y]=x;
        if(rank[x]==rank[y])
            rank[x]++;
    }
}

bool same(int x,int y)
{
    return find(x)==find(y);
}

int kruskal(int d)
{
    inti(110);
    int res=0;
    for(int i=0;i<m;i++)
    {
        edge e=ed[i];
        if(!same(e.from,e.to)&&i!=d)
        {
            if(d==-1)
                used[cnt++]=i;
            unite(e.from,e.to);
            res+=e.w;
        }
    }
    return res;
}

int main()
{
    while(cin>>n)
    {
        int from,to,w,ans,flag=0;
        cnt=0;
        memset(dist,0,sizeof(dist));
        memset(used,0,sizeof(used));
        for(int i=0;i<n;i++)
            mincost[i]=1000000100;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                cin>>dist[i][j];
        //sort(ed,ed+m,comp);
        //ans=kruskal(-1);
        int res=0;
        mincost[0]=0;
        while(1)
        {
            int v=-1;
            for(int u=0;u<n;u++)
                if(!used[u]&&(mincost[u]<mincost[v]||v==-1))
                    v=u;
            if(v==-1)
                break;
            used[v]=true;
            res+=mincost[v];
            for(int u=0;u<n;u++)
                mincost[u]=min(mincost[u],dist[u][v]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

J. POJ 3026  Borg Maze

题意:在一个迷宫内有数个A点,你需要从S点到达N点问你最小花费是多少。

思路:对所有的S点和A点进行一次bfs,求出任意两点之间的最短距离,然后最小生成树。注意输入,有陷阱。

#include <stdio.h>
#include <iostream>
#include <queue>
using namespace std;
#define MAXV 102
#define inf 1<<29

typedef struct{
    int x,y;
}Point;

Point point[MAXV];
char s[MAXV][MAXV];
int m,n,psum,map[MAXV][MAXV];
int mincost[MAXV];
int used[MAXV];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};

void findpoint(){
    int i,j;
    psum=1;
    for(i=0;i<m;i++)
        for(j=0;j<n;j++)
            if(s[i][j]=='A'){
                point[psum].x=i;
                point[psum++].y=j;
            }else if(s[i][j]=='S'){
                point[0].x=i;
                point[0].y=j;
            }
}

void bfs(int start){
    int discover[MAXV][MAXV],dis[MAXV][MAXV];
    queue <int> q;
    int i,a,b,tx,ty;

    for(i=0;i<m;i++)
        for(int j=0;j<n;j++){
            dis[i][j]=0;
            discover[i][j]=0;
        }

    a=point[start].x;
    b=point[start].y;
    q.push(a);
    q.push(b);
    discover[a][b]=true;

    while(!q.empty()){
        a=q.front();q.pop();
        b=q.front();q.pop();
        for(i=0;i<4;i++){
            tx=a+dx[i];
            ty=b+dy[i];
            if(!discover[tx][ty] && tx>=0 && tx<n && ty>=0 && ty<m && s[tx][ty]!='#'){
                dis[tx][ty]=dis[a][b]+1;
                discover[tx][ty]=true;
                q.push(tx);
                q.push(ty);
            }
        }
    }
    for(i=0;i<psum;i++){
        int t=dis[point[i].x][point[i].y];
        if(t) map[start][i]=t;
    }
}

void prim(){
    int i,j,v;
    int d[MAXV],vis[MAXV];
    for(i=0;i<psum;i++){
        d[i]=map[0][i];
        vis[i]=0;
    }

    for(i=0;i<psum;i++){
        int min=inf;
        for(j=0;j<psum;j++)
            if(!vis[j] && d[j]<min){
                min=d[j];
                v=j;
            }

            vis[v]=1;

            for(j=0;j<psum;j++){
                if(!vis[j] && map[v][j]<d[j])
                    d[j]=map[v][j];
            }
    }
    int ans=0;
    for(i=0;i<psum;i++) ans+=d[i];
    printf("%d\n",ans);
}

int main(){
    int t,i;
    scanf("%d\n",&t);
    while(t--){
        gets(s[0]);
        sscanf(s[0],"%d%d",&n,&m);

        for(i=0;i<m;i++) gets(s[i]);
        findpoint();                    //找到点的坐标
        for(i=0;i<psum;i++) bfs(i);              //计算距离

        for(int i=0;i<psum;i++)
        {
            mincost[i]=1000000000;
            used[i]=false;
        }
        int res=0;
        mincost[0]=0;

        while(1)
        {
            int v=-1;
            for(int u=0;u<psum;u++)
                if(!used[u]&&(mincost[u]<mincost[v]||v==-1))
                    v=u;
            if(v==-1)
                break;
            used[v]=true;
            res+=mincost[v];
            for(int u=0;u<psum;u++)
                mincost[u]=min(mincost[u],map[v][u]);
        }
        printf("%d\n",res);                     //最小生成树
    }
    return 0;
}
View Code

K. POJ 1679  The Unique MST

题意:问你最小生成树是不是唯一的。

参看:http://www.cnblogs.com/onlyAzha/p/4793031.html  次小生成树的两种做法。

L. HDU 1233  还是畅通工程

模板题

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

int dist[110][110];
int mincost[110];
bool used[110];

struct Point{
    int x,y;
}point[110];

double fun(int i,int j)
{
    return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)*1.0+(point[i].y-point[j].y)*(point[i].y-point[j].y)*1.0);
}
int T,n;

int main()
{
    while(scanf("%d",&n)&&n)
    {
        int from,to,w;
        for(int i=0;i<n*(n-1)/2;i++)
        {
            scanf("%d%d%d",&from,&to,&w);
            dist[from][to]=dist[to][from]=w;
        }
        for(int i=1;i<=n;i++)
        {
            used[i]=false;
            mincost[i]=100000000;
        }

        int res=0;
        mincost[1]=0;

        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            res+=mincost[v];
            for(int u=1;u<=n;u++)
                mincost[u]=min(mincost[u],dist[v][u]);
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

N. HDU 1875  畅通工程再续

题意:n个点,任意两个距离不小于10也不大于1000的岛都可以连一条权值为距离*100的边,然后最小生成树。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<set>

using namespace std;

double dist[110][110];
double mincost[110];
bool used[110];

struct Point{
    int x,y;
}point[110];

double fun(int i,int j)
{
    return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)*1.0+(point[i].y-point[j].y)*(point[i].y-point[j].y)*1.0);
}
int T,n;

int main()
{
    cin>>T;
    while(T--)
    {
        int flag=0;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>point[i].x>>point[i].y;
            for(int j=1;j<i;j++)
                dist[i][j]=dist[j][i]=fun(i,j);
        }
        for(int i=1;i<=n;i++)
        {
            flag=0;
            for(int j=1;j<=n;j++)
            {
                if(i!=j&&(10.0<=dist[i][j]&&dist[i][j]<=1000.0))
                    flag=1;
                if(i!=j&&dist[i][j]<10)
                    dist[i][j]=1000.0;
            }
            if(!flag)
                break;
        }
        if(!flag)
        {
            printf("oh!\n");
            continue;
        }

        for(int i=1;i<=n;i++)
        {
            used[i]=false;
            mincost[i]=100000000.0;
        }

        double res=0;
        mincost[1]=0.0;

        while(1)
        {
            int v=-1;
            for(int u=1;u<=n;u++)
                if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                    v=u;
            if(v==-1)
                break;
            used[v]=1;
            res+=mincost[v];
            for(int u=1;u<=n;u++)
                mincost[u]=min(mincost[u],dist[v][u]);
        }
        printf("%.1f\n",res*100);
    }
    return 0;
}
View Code

 

posted on 2015-09-11 10:32  onlyAzha  阅读(164)  评论(0编辑  收藏  举报

导航