最小生成树专题

专题地址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66965#overview

A 裸的最小生成树,Kruskal算法。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=3000;
const int maxm=100000;
int parent[maxn];
int tot;

struct Edge
{
    int u,v,w;
}edge[maxm];

void addedge(int u,int v,int w)
{
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot++].w=w;
}

int find(int x)
{/*
    if(parent[x]==x)
        return x;
    parent[x]=find(parent[x]);
    return parent[x];*/
    return parent[x]==x?x:find(parent[x]);
}
bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}

void init(int n)
{
    for(int i=0;i<=n;i++)
        parent[i]=i;
    tot=0;
}

int kruscal(int n)
{
    sort(edge,edge+tot,cmp);
    int cnt=0;
    int ans=0;
    for(int i=0;i<tot;i++)
    {
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int t1=find(u);
        int t2=find(v);
        if(t1!=t2)
        {
            parent[t1]=t2;
            ans+=w;
            cnt++;
        }
        if(cnt==n-1)
            break;
    }
    if(cnt<n-1)
        return -1;
    else
        return ans;
}

int n;

int main()
{
    while(rd(n)!=EOF&&n)
    {
        char ch;int m;
        init(n);
        for(int i=1;i<=n-1;i++)
        {
            cin>>ch;
            int u=ch-'A'+1;
            rd(m);
            char to;int w;
            while(m--)
            {
                cin>>to;
                int v=to-'A'+1;
                rd(w);
                addedge(u,v,w);
            }
        }
        printf("%d\n",kruscal(n));
    }
    return 0;
}

B 裸的最小生成树,用Prim算法。注意两点之间可能有多条边。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=60;
const int inf=0x3f3f3f3f;
int cost[maxn][maxn];
bool vis[maxn];
int lowc[maxn];
int n,m;

int Prim(int cost[][maxn],int n)
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=true;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)
    {
        int minc=inf;
        int p=-1;
        for(int j=0;j<n;j++)
            if(!vis[j]&&minc>lowc[j])
        {
            minc=lowc[j];
            p=j;
        }
        if(minc==inf) return -1;//不连通
        ans+=minc;
        vis[p]=true;
        for(int j=0;j<n;j++)
            if(!vis[j]&&lowc[j]>cost[p][j])
            lowc[j]=cost[p][j];
    }
    return ans;
}


int main()
{
    while(rd(n)!=EOF&&n)
    {
        rd(m);
        int u,v,w;
        memset(cost,inf,sizeof(cost));
        while(m--)
        {
            rd3(u,v,w);
            u--;v--;
            if(w<cost[u][v])
            {
                cost[u][v]=w;
                cost[v][u]=w;
            }
        }
        printf("%d\n",Prim(cost,n));
    }
    return 0;
}

C 空间中有n个小球,给出每个小球的三维坐标和半径,小球可以相互接触也可以重叠,n个小球连接起来,求最小的通道长度。计算任意两个小球之间的距离(注意接触或者重叠距离为0),然后用最小生成树就可以了。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=110;
double cost[maxn][maxn];
bool vis[maxn];
const double inf=1000000;
const double eps=1e-8;
double lowc[maxn];

struct Point
{
    double x,y,z,r;
    void input()
    {
        scanf("%lf%lf%lf%lf",&x,&y,&z,&r);
    }
    double distance(Point p)
    {
        double dis=sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)+(z-p.z)*(z-p.z));
        if(dis-r-p.r>eps)
            return dis-r-p.r;
        else
            return 0;
    }
}point[maxn];
int n;

double Prim(double cost[][maxn],int n)
{
    memset(vis,0,sizeof(vis));
    double ans=0;
    vis[0]=1;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)
    {
        double minc=inf;
        int p=-1;
        for(int j=0;j<n;j++)
        {
            if(minc>lowc[j]&&!vis[j])
            {
                p=j;
                minc=lowc[j];
            }
        }
        ans+=minc;
        vis[p]=1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }
    }
    return ans;
}
int main()
{
    while(rd(n)!=EOF&&n)
    {
        for(int i=0;i<n;i++)
        {
            point[i].input();
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                cost[i][j]=inf;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            double dis=point[i].distance(point[j]);
            if(dis<cost[i][j])
                cost[i][j]=cost[j][i]=dis;
        }
        printf("%.3f\n",Prim(cost,n));
    }
    return 0;
}

D 有N个村庄,给定任意两个村庄之间的距离,并且给出一些已经存在的路(边),问最少共再需要多长的路使得N个村庄联通。最小生成树,本题就是有个条件已经存在一些边了,用Prim算法求的是总长度,且每次都是找的最小的边,那么只要把已经存在的边权值设为0就好了,因为每次都是找最小的边,那么这些边肯定会被选择的,而且用prim算法求完后也正好是题目所求的长度。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=104;
const int inf=0x3f3f3f3f;
int cost[maxn][maxn];
int lowc[maxn];
bool vis[maxn];
int n,m;

int Prim(int cost[][maxn],int n)
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=true;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)//寻找这么多次
    {
        int p=-1,minc=inf;
        for(int j=0;j<n;j++)
        {
            if(minc>lowc[j]&&!vis[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        vis[p]=1;
        ans+=minc;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }
    }
    return ans;
}


int main()
{
    rd(n);
    memset(cost,inf,sizeof(cost));
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
    {
        rd(cost[i][j]);
    }
    rd(m);
    int u,v;
    while(m--)
    {
        rd2(u,v);
        u--;v--;
        cost[u][v]=cost[v][u]=0;
    }
    printf("%d\n",Prim(cost,n));
    return 0;
}

E 有n个节点,每个节点都有一个权值,然后给出任意两个节点之间的距离,要在两个节点之间连边,使得这n个节点联通,连边的代价除了两点之间边的距离之外还有两点各自的权值之和。在cost矩阵中,加入两个点的权值,然后求最小生成树。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=1004;
const int inf=0x3f3f3f3f;
int cost[maxn][maxn];
int lowc[maxn];
bool vis[maxn];
int val[maxn];
int n,m;

int Prim(int cost[][maxn],int n)
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=true;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)//寻找这么多次
    {
        int p=-1,minc=inf;
        for(int j=0;j<n;j++)
        {
            if(minc>lowc[j]&&!vis[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        vis[p]=1;
        ans+=minc;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }
    }
    return ans;
}


int main()
{
    int t;rd(t);
    while(t--)
    {
        rd(n);
        memset(cost,inf,sizeof(cost));
        for(int i=0;i<n;i++)
            rd(val[i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            rd(cost[i][j]);
            cost[i][j]+=val[i]+val[j];
        }
        printf("%d\n",Prim(cost,n));
    }
    return 0;
}

F 给出N个长度为7的字符串,两个字符串之间定义距离为 两个字符串每个位置字母不同的个数,一个字符串可以生成另一个字符串,代价为两个字符串的距离,问这n个字符串最小的代价。  把任意两个字符串之间的距离求出来,然后进行最小生成树。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=2002;
const int inf=0x3f3f3f3f;
int cost[maxn][maxn];
int lowc[maxn];
bool vis[maxn];
string str[maxn];
int n;

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

int Prim(int cost[][maxn],int n)
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=1;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)
    {
        int minc=inf,p=-1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        vis[p]=1;
        ans+=minc;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }

    }
    return ans;
}


int main()
{
    while(rd(n)!=EOF&&n)
    {
        for(int i=0;i<n;i++)
            cin>>str[i];
        memset(cost,inf,sizeof(cost));
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
        {
            int w=cal(i,j);
            if(w<cost[i][j])
                cost[i][j]=cost[j][i]=w;
        }
        printf("The highest possible quality is 1/%d.\n",Prim(cost,n));

    }
    return 0;
}

G 题意为有N个部落以及它们的坐标,然后有P个卫星,如果两个部落都有一个卫星的话,那么这两个部落无论距离多远都能通信,否则,两个部落只能依靠无线电通信,条件是只有两个部落的距离不超过D,两个部落才能通信,求最小的D,使得这N个部落都能通信。也就是在这N个部落里面找N-1条边,使得他们相互连通,P个卫星肯定是放在距离最大的村庄上,这样省去了P-1条边,所以我们只要再找N-1-(P-1)条边就好了,要求最小的D,也就是求这N-1-(P-1)条边里面的最大边的值。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=510;
const double inf=100000000.0;
double cost[maxn][maxn];
double lowc[maxn];
bool vis[maxn];
double edge[maxn];

struct Point
{
    double x,y;
    void input()
    {
        scanf("%lf%lf",&x,&y);
    }
    double distance(Point p)
    {
        return hypot(x-p.x,y-p.y);
    }
}point[maxn];
int n,P;

double Prim(double cost[][maxn],int n)
{
    memset(vis,0,sizeof(vis));
    vis[0]=1;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    int cnt=0;
    for(int i=1;i<n;i++)
    {
        double minc=inf;
        int p=-1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        edge[cnt++]=minc;
        vis[p]=1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }
    }
    sort(edge,edge+cnt);//别忘了排序
    return edge[cnt-P];
}

int main()
{
    int t;rd(t);
    while(t--)
    {
        rd2(P,n);
        for(int i=0;i<n;i++)
            point[i].input();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            cost[i][j]=inf;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            double dis=point[i].distance(point[j]);
            if(dis<cost[i][j])
                cost[i][j]=cost[j][i]=dis;
        }
        printf("%.2f\n",Prim(cost,n));
    }
    return 0;
}

H 给定n个节点以及这n个节点的坐标,另外给出了m条已经现有的边,求要使这n个点连通,还需要建多少边,输出需要建边的两个端点编号。和上面有个题差不多,也是把给出的边的权值设置为0,然后pre[i]表示i节点所在边的另一个节点,最小生成树选择一条边以后,输出pre[p], p 就可以了。为了避免已经存在的边输出,判断一下是否cost[i][j] >0。因为刚才已存在的边已经赋值为0了。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=1000;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int cost[maxn][maxn];
int lowc[maxn];
int pre[maxn];
int n,m;

struct Point
{
    int x,y;
    int distance(Point p)
    {
        return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
    }
}point[maxn];

void Prim()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<n;i++)
    {
        lowc[i]=cost[0][i];
        pre[i]=0;
    }
    vis[0]=1;int p=-1;
    for(int i=1;i<n;i++)
    {
        int minc=inf;

        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        vis[p]=1;
        if(cost[pre[p]][p])
            printf("%d %d\n",pre[p]+1,p+1);
        for(int j=0;j<n;j++)
        {
            if(lowc[j]>cost[p][j]&&!vis[j])
            {
                lowc[j]=cost[p][j];
                pre[j]=p;
            }
        }
    }
}

int main()
{
        rd(n);
        memset(cost,inf,sizeof(cost));
        for(int i=0;i<n;i++)
            rd2(point[i].x,point[i].y);
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
        {
            int dis=point[i].distance(point[j]);
            if(cost[i][j]>dis)
                cost[i][j]=cost[j][i]=dis;
        }
        rd(m);
        int u,v;
        while(m--)
        {
            rd2(u,v);
            u--;v--;
            cost[u][v]=cost[v][u]=0;
        }
        Prim();
    return 0;
}
I 裸的最小生成树

const int maxn=5010;
int parent[110];
int n;

struct Node
{
    int from,to,edge;
}node[maxn];

void init(int n)
{
    for(int i=1;i<=n;i++)
        parent[i]=i;
}

int find(int x)
{
    return parent[x]==x?x:find(parent[x]);
}

bool cmp(Node a,Node b)
{
    if(a.edge<b.edge)
        return true;
    return false;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        int m=1;
        int len;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&len);
                if(i<j)
                {
                    int temp=m;
                    node[temp].from=i;
                    node[temp].to=j;
                    node[temp].edge=len;
                    m++;
                }
            }
        init(n);
        m=n*(n-1)/2;
        len=0;
        sort(node+1,node+1+m,cmp);
        for(int i=1;i<=m;i++)
        {
            int x=find(node[i].from);
            int y=find(node[i].to);
            if(x==y)
                continue;
            else
            {
                len+=node[i].edge;
                parent[x]=y;
            }
        }
        printf("%d\n",len);
    }
    return 0;
}

J 在一个迷宫里面#代表不可走,空白可走,存在字母一个‘S' 和多个’A‘ ,要从S出发找到所有的A,可以从S出发分成多路去找A, 问最少的步数和。也就是求最小生成树,节点为S和A,需要知道任意两个节点的最短距离,因为对每个点都用bfs,找出任意两个点之间的最短距离,然后使用最小生成树就可以了。 注意:输入格式,输入迷宫的大小n*m的时候很坑,要使用字符串格式...

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=110;
const int inf=0x3f3f3f3f;
int cost[maxn][maxn];
int lowc[maxn];
bool vis[maxn][maxn];
int tot;
char mp[60][60];
int n,m;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};

struct Point
{
    int i,j;
}point[maxn];

void BFS(int from,int x,int y)
{
    memset(vis,0,sizeof(vis));
    int step[maxn][maxn];
    step[x][y]=0;
    vis[x][y]=1;
    queue<Point>q;
    Point a;
    a.i=x;a.j=y;
    q.push(a);
    while(!q.empty())
    {
        Point first=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int newx=first.i+dx[i];
            int newy=first.j+dy[i];
            if(mp[newx][newy]!='#'&&!vis[newx][newy]&&newx>=0&&newx<n&&newy>=0&&newy<m)
            {
                a.i=newx;a.j=newy;
                vis[newx][newy]=1;
                q.push(a);
                step[newx][newy]=step[first.i][first.j]+1;
            }
        }
    }
    for(int i=0;i<tot;i++)
    {
        cost[from][i]=step[point[i].i][point[i].j];
    }
}

int Prime(int cost[][maxn],int n)
{
    bool vis[maxn];
    int ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=1;
    for(int i=1;i<tot;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)
    {
        int minc=inf,p=-1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                p=j;
                minc=lowc[j];
            }
        }
        ans+=minc;
        vis[p]=1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&lowc[j]>cost[p][j])
                lowc[j]=cost[p][j];
        }
    }
    return ans;
}



int main()
{
    int t;rd(t);
    getchar();
    while(t--)
    {
        gets(mp[0]);
        sscanf(mp[0],"%d%d",&m,&n);//这里的格式
        tot=1;
        for(int i=0;i<n;i++)
        {
            gets(mp[i]);
            int len=strlen(mp[i]);
            for(int k=0;k<len;k++)
            {
                if(mp[i][k]=='A')
                    point[tot].i=i,point[tot++].j=k;
                else if(mp[i][k]=='S')
                    point[0].i=i,point[0].j=k;
            }
        }
        //construct cost[][]
        memset(cost,inf,sizeof(cost));
        for(int i=0;i<tot;i++)
            BFS(i,point[i].i,point[i].j);
        /*
        for(int i=0;i<tot;i++)
        {
            for(int j=0;j<tot;j++)
                cout<<cost[i][j]<<"       ";
                cout<<endl;
        }
        */
        //进行prim
        printf("%d\n",Prime(cost,tot));
    }
    return 0;
}

K 次小生成树,如果最小生成树唯一的话,输出最小权值,否则输出not unique。

http://blog.chinaunix.net/uid-25324849-id-2182922.html
次小生成树
求最小生成树时,用maxval[i][j],表示最小生成树中i到j路径中的最大边权
求完后,直接枚举所有不在最小生成树中的边(比如该边的两端是i,j,
然后替换掉路径i,j中最大边权的边,更新答案
点的编号从0开始。
为什么可以替换、
求完最小生成树后,从i到j有路径,那么路径上的点两两可以相互到达,如果去掉最大边
,那么该路径就切成了两部分,而加入不在最小生成树的一条边(比如该边地两端是i,j)
,那么又把这两部分重新串起来了,重新变得两两可以相互到达。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=110;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int lowc[maxn];
int pre[maxn];
int cost[maxn][maxn];
int maxval[maxn][maxn];
bool used[maxn][maxn];

int Prim(int cost[][maxn],int n)
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    memset(maxval,0,sizeof(maxval));
    memset(used,0,sizeof(used));
    vis[0]=true;
    pre[0]=-1;
    for(int i=1;i<n;i++)
    {
        lowc[i]=cost[0][i];
        pre[i]=0;
    }
    lowc[0]=0;
    for(int i=1;i<n;i++)
    {
        int minc=inf,p=-1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        if(minc==inf)
            return -1;
        ans+=minc;
        vis[p]=true;
        used[p][pre[p]]=used[pre[p]][p]=true;
        for(int j=0;j<n;j++)
        {
            if(vis[j])
                maxval[j][p]=maxval[p][j]=max(maxval[j][pre[p]],lowc[p]);
            //为什么这么写,vis[j]代表那么节点已经访问过了,该条边是不能加的,有没有边都没关系,否则成了环,
            //那么在路径j->p中,j是路径的起点,p就是路径的终点,而maxval[j][pre[p]],就是起点j到p的上一个节点
            //组成的路径的最大边权值,lowc[p],则是带有p的那一边的权值,二者最大值,就是路径j->p的最大权值
            //比如 1->2->3->4-------->1,虚线代表有边或者没有边都行,那么此时p=4,j=1, pre[p]=3,
            //maxval[j][pre[p]]=maxval[1][3],也就是路径1到3上的最大边的权值,lowc[4]则是3到4上权值,二者的最大值
            //则是路径1->4的最大边的权值。
            if(!vis[j]&&lowc[j]>cost[p][j])
            {
                lowc[j]=cost[p][j];
                pre[j]=p;
            }
        }
    }
    return ans;
}

int ans;//保存最小生成树的值

int smst(int cost[][maxn],int n)//次小生成树
{
    int minc=inf;
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
    {
        if(cost[i][j]!=inf&&!used[i][j])//i,j之间有边,且该边没有在最小生成树中
        {
            minc=min(minc,ans+cost[i][j]-maxval[i][j]);//用该边替换最小生成树中i,j路径中的最大边
        }
    }
    if(minc==inf)
        return -1;
    return minc;
}
int n,m;

int main()
{
    int t;rd(t);
    while(t--)
    {
        rd2(n,m);
        memset(cost,inf,sizeof(cost));
        int u,v,w;
        while(m--)
        {
            rd3(u,v,w);
            u--;
            v--;
            if(w<cost[u][v])
                cost[u][v]=cost[v][u]=w;
        }
        ans=Prim(cost,n);
        if(ans==smst(cost,n))
            printf("Not Unique!\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

L 裸最小生成树

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=110;
const int maxm=5000;

struct Edge
{
    int u,v,w;
}edge[maxm];
int tot;//边的个数
int parent[maxn];

void addege(int u,int v,int w)
{
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot++].w=w;
}

void init(int n)
{
    tot=0;
    for(int i=0;i<=n;i++)
        parent[i]=i;
}

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

bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}

int n;

int kruskal(int n)
{
    sort(edge,edge+tot,cmp);
    int cnt=0;//计算加入的边数
    int ans=0;
    for(int i=0;i<tot;i++)
    {
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int t1=find(u);
        int t2=find(v);
        if(t1!=t2)
        {
            ans+=w;
            parent[t1]=t2;
            cnt++;
        }
        if(cnt==n-1)
            break;
    }
    if(cnt<n-1)
        return -1;//不连通
    else
        return ans;
}

int main()
{
    while(rd(n)!=EOF&&n)
    {
        init(n);
        int u,v,w;
        for(int i=1;i<=n*(n-1)/2;i++)
        {
            rd3(u,v,w);
            addege(u,v,w);
        }
        printf("%d\n",kruskal(n));
    }
    return 0;
}

N 同裸,浮点数。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <cmath>
#include <iomanip>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=110;

struct Point
{
    double x,y;
    double distance(Point p)
    {
        return hypot(x-p.x,y-p.y);
    }
}point[maxn];

const double inf=100000.0;
bool vis[maxn];
double lowc[maxn];
double cost[maxn][maxn];

double Prim(double cost[][maxn],int n)
{
    double ans=0;
    memset(vis,0,sizeof(vis));
    vis[0]=true;
    for(int i=1;i<n;i++)
        lowc[i]=cost[0][i];
    for(int i=1;i<n;i++)
    {
        double minc=inf;
        int p=-1;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                p=j;
            }
        }
        if(minc==inf)
            return -1.0;
        ans+=minc;
        vis[p]=true;
        for(int j=0;j<n;j++)
            if(!vis[j]&&lowc[j]>cost[p][j])
            lowc[j]=cost[p][j];
    }
    return ans;
}
int n;

int main()
{
    int t;rd(t);
    while(t--)
    {
        rd(n);
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&point[i].x,&point[i].y);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
                cost[i][j]=inf;
        }
        /*
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
                cout<<cost[i][j]<<" ";
            cout<<endl;
        }
        */
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                double dis=point[i].distance(point[j]);
                if(dis<cost[i][j]&&dis>=10&&dis<=1000)
                {
                    cost[i][j]=dis;
                    cost[j][i]=dis;
                }
            }
        }
        double ans=Prim(cost,n);
        if(ans==-1.0)
            printf("oh!\n");
        else
            printf("%.1f\n",ans*100);
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值