UVA 10462 Is There A Second Way Left?(Kruskal算法/Prim算法)

【题意】

给一个含有n个点,cnt条边的图(1<= n <= 100,0 <= cnt <= 200),问次小生成树是多少,如果无法生成次小生成树,输出No second way,如果最小生成树也无法生成,输出No way。


【思路】

直接Kruskal或Prim算法求一下最小生成树,然后枚举这棵生成树中的边剔除来生成次小生成树。注意Prim算法,若用链式前向星来建图,由于是双向边,所以数组还得开得更大些。


【代码1】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=105,INF=0x3f3f3f3f;

struct edge{
    int from,to,cost;

    bool operator<(const edge &another)const
    {
        return cost<another.cost;
    }
};

int t,n,cnt;
int fa[MAXN];
edge e[2*MAXN];
bool used[2*MAXN];

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

int findfather(int x)
{
    return (x==fa[x]?x:fa[x]=findfather(fa[x]));
}

int kruskal(int x)
{
    init();
    if(x==0){
        memset(used,false,sizeof(used));
        int tot=0,ans=0;
        for(int i=1;i<=cnt;i++){
            int p,q;
            p=findfather(e[i].from);
            q=findfather(e[i].to);
            if(p!=q){
                fa[q]=p;
                used[i]=true;
                tot++;
                ans+=e[i].cost;
                if(tot==n-1)return ans;
            }
        }
        return INF;
    }
    else{
        int tot=0,ans=0;
        for(int i=1;i<=cnt;i++){
            if(i==x)continue;
            int p,q;
            p=findfather(e[i].from);
            q=findfather(e[i].to);
            if(p!=q){
                fa[q]=p;
                tot++;
                ans+=e[i].cost;
                if(tot==n-1)return ans;
            }
        }
        return INF;
    }
}

int main()
{
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++){
        scanf("%d %d",&n,&cnt);
        for(int i=1;i<=cnt;i++){
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            e[i].from=a;
            e[i].to=b;
            e[i].cost=c;
        }
        sort(e+1,e+1+cnt);
        if(kruskal(0)==INF&&n>1){
            printf("Case #%d : No way\n",kase);
            continue;
        }
        int ans=INF;
        for(int i=1;i<=cnt;i++)
            if(used[i])ans=min(ans,kruskal(i));
        if(ans==INF)
            printf("Case #%d : No second way\n",kase);
        else
            printf("Case #%d : %d\n",kase,ans);
    }
    return 0;
}


【代码2】

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int MAXN=105,INF=0x3f3f3f3f;

struct edge{
    int from,to,cost,next;
};

int t,n,m,cnt,minpoint,minlen,minnum;
int head[MAXN];
bool visited[MAXN],used[4*MAXN];
edge e[4*MAXN];
queue<int> q;

void addedge(int from,int to,int cost)
{
    cnt++;
    e[cnt].to=to;
    e[cnt].cost=cost;
    e[cnt].next=head[from];
    head[from]=cnt;
}

void findone(int x)
{
    int y=(x&1?x+1:x-1);
    if(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i!=0;i=e[i].next){
            if(i==x||i==y)continue;
            int v=e[i].to;
            if(!visited[v]&&minlen>e[i].cost){
                minlen=e[i].cost;
                minpoint=v;
                minnum=i;
            }
        }
        findone(x);
        q.push(u);
    }
}

int prim(int x)
{
    memset(visited,false,sizeof(visited));
    while(!q.empty())q.pop();
    q.push(1);
    visited[1]=true;
    int ans=0,tot=1;
    minlen=INF;
    findone(x);
    if(x==0){
        memset(used,false,sizeof(used));
        while(minlen!=INF){
            int v=minpoint;
            q.push(v);
            visited[v]=true;
            used[minnum]=true;
            if(minnum&1)
                used[minnum+1]=true;
            else
                used[minnum-1]=true;
            ans+=minlen;
            tot++;
            if(tot==n)break;
            minlen=INF;
            findone(x);
            v=minpoint;
        }
    }
    else{
        while(minlen!=INF){
            int v=minpoint;
            q.push(v);
            visited[v]=true;
            ans+=minlen;
            tot++;
            if(tot==n)break;
            minlen=INF;
            findone(x);
        }
    }
    if(tot!=n)ans=INF;
    return ans;
}

int main()
{
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++){
        scanf("%d %d",&n,&m);
        memset(head,0,sizeof(head));
        cnt=0;
        for(int i=1;i<=m;i++){
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
        if(prim(0)==INF){
            printf("Case #%d : No way\n",kase);
            continue;
        }
        int ans=INF;
        for(int i=1;i<=cnt-1;i+=2)
            if(used[i])ans=min(ans,prim(i));
        if(ans==INF)
            printf("Case #%d : No second way\n",kase);
        else
            printf("Case #%d : %d\n",kase,ans);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值