修路方案

描述

南将军率领着许多部队,它们分别驻扎在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)
样例输入
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出
No
Yes


这道题其实是求最小生成树,我认为用并查集做比较方便一点,

在求最小生成树时可以保存每一条线段,然后删除其中一条再

找一次最小生成树看是否答案一样。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define INF 0x7fffffff
const int N = 200005, maxn = 505;
int m, father[maxn], n, pos[maxn];
struct node
{
    int A, B, L, flag;
    friend bool operator < ( node n1, node n2 ) //根据长度排序
    {
        return n1.L < n2.L;
    }
} a[N];
void init ( )
{
    for ( int i = 1; i <= n; i ++ )
        father[i] = i;
}
int find ( int x )  //并查集
{
    int r = x, i, j;
    while ( r != father[r] )
        r = father[r];
    i = x;
    while ( i != r )
    {
        j = father[i];
        father[i] = r;
        i = j;
    }
    return r;
}
int kruskal ( )
{
    init ( );
    int ret = 0, j = 0; //j统计线段的条数
    for ( int i = 0; i < m; i ++ )
    {
        if ( a[i].flag )    //标记此线段被删除
            continue ;
        int fx = find ( a[i].A );
        int fy = find ( a[i].B );
        if ( fx != fy )
        {
            father[fx] = fy;
            ret = ret+a[i].L;   //统计长度和
            j ++;
        }
        if ( j >= n-1 ) //线段条数为n-1时 证明已经全部找齐
            break ;
    }
    if ( j < n-1 )
        return INF;
    return ret;
}
void print ( int n )
{
    for ( int i = 0; i < n; i ++ )
        printf ( "%d ", pos[i] );
    printf ( "\n" );
}
int main ( )
{
    int T, cnt, mn, ok;
    scanf ( "%d", &T );
    while ( T -- )
    {
        cnt = mn = ok = 0;
        scanf ( "%d%d", &n, &m );
        for ( int i = 0; i < m; i ++ )
        {
            scanf ( "%d%d%d", &a[i].A, &a[i].B, &a[i].L );
            a[i].flag = 0;
        }
        std :: sort ( a, a+m );
        init ( );
        for ( int i = 0; i < m; i ++ )  //不删除点做并查集
        {
            int fx = find ( a[i].A ), fy = find ( a[i].B );
            if ( fx != fy )
            {
                father[fx] = fy;
                mn = mn+a[i].L;
                pos[cnt ++] = i;    //保存最短的线段下标
            }
            if ( cnt >= n-1 )
                break ;
        }
        //print ( cnt );
        for ( int i = 0; i < cnt; i ++ )
        {
            int p = pos[i];
            a[p].flag = 1;  //删除此点
            //printf ( "%d\n", kruskal ( ) );
            if ( mn == kruskal ( ) )
            {
                ok = 1;
                break ;
            }
            a[p].flag = 0;  //注意还原
        }
        printf ( ok ? "Yes\n" : "No\n" );
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值