求次最小生成树,南阳118题 修路方案

思路:这个题就是求次最小生成树,解法 先求一遍最小生成树,然后在去掉其中一条边求。如果去掉一条边求得的生成树的权值等于之前求的的那条则表明有多条则输出yes,下一次循环把这条边加上,在去掉下一条。需要注意的是有可能去掉这一条边就构不成最小生成树了 不联通的。

我这里用的是克鲁斯卡尔算法 。这题是用并查集实现的,要是不知道并查集的朋友可以先百度 看看并查集 这个算法;

修路方案

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 5
描述

南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路。

现在已经知道哪些城市之间可以修路,如果修路,花费是多少。

现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少。

但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧。

输入
第一行输入一个整数T(1<T<20),表示测试数据的组数
每组测试数据的第一行是两个整数V,E,(3<V<500,10<E<200000)分别表示城市的个数和城市之间路的条数。数据保证所有的城市都有路相连。
随后的E行,每行有三个数字A B L,表示A号城市与B号城市之间修路花费为L。
输出
对于每组测试数据输出Yes或No(如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)

代码:

/*
思路:这道题让找两种以上的最小花费方案,需要找次小生成树,
(1)先找出最小生成树,并把组成最小生成树的边标记,
(2)然后每次删除最小生成树中的一条边,看其他边组成的最小生成树等不等与第一次算的,如果等于就可以break,说明已经找了两种最小方案
(3)但是要注意可能删除一个边后其他边组不成一个连通的通路*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef struct
{
    int start;
    int end;
    int w;
    bool flag;
}bian;//存储每条边
int n,m;
bian gc[200000];
int pre[501];
int a[501];//a数组记录最小生成树的边。
bool fff;
bool cmp(bian a,bian b) { return a.w<b.w; }
int find(int x)
{
    int r=x;
    while(r!=pre[r])
    {
        r=pre[r];
    }
    int j=x,i;
    while(j!=r)
    {
        i=pre[j];
        pre[j]=r;
        j=i;
    }
    return r;
}
int kruskal()//并查集实现。
{  int top=n;int count=1;//一开始每个顶点都是一颗树,然后合并一个top减少一颗
    for(int i=1;i<=n;i++)
    {
        pre[i]=i;
    }
    int sum=0;
    for(int i=0;i<m;i++)
    {
        int f1=find(gc[i].start);
        int f2=find(gc[i].end);
        if(f1!=f2)
        {
            pre[f2]=f1;sum=sum+gc[i].w;top--;//判断最后是否连通
            a[count++]=i;//保存第i条边
        }
        if(top==1) break;
    }
for(int i=count-1;i>=1;i--)
  {
      gc[a[i]].flag=1;//将第i条边去掉 下面是在求一次最小生成树
    for(int i=1;i<=n;i++)
    {
        pre[i]=i;
    }
    int sum1=0;top=n;
    for(int j=0;j<m;j++)
    {  if(gc[j].flag==0)
       {

          int f1=find(gc[j].start);
          int f2=find(gc[j].end);
          if(f1!=f2)
           {
              pre[f2]=f1;sum1=sum1+gc[j].w;top--;//判断最后是否连通

           }
          if(top==1) break;
      }
    }
    if(sum1==sum&&top==1) {printf("Yes\n"); fff=0;return 1;}//判断是不是相等。有可能删除边之后构不成最小生成树。但是刚好又和之前统计的权值又相同;
    gc[a[i]].flag=0;

  }


}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            bian a={x,y,w,0};
            gc[i]=a;
        }
          sort(gc,gc+m,cmp);
          fff=1;
        kruskal();
        if(fff) printf("No\n");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值