Uva 10462 Is There A Second Way Left? Kruskal求次小生成树

这两天做了三题次小生成树包括上篇博客都是用Prim算法写的;孤陋寡闻的我还以为smst就是用prim求;

直到碰到这题,很裸的次小生成树!但和以往都不一样的是它会有重边!

这样以来用prim算法考虑的话,used[ i ][ j ]数组就无法做标记;在删除边的时候也不知道怎么删除。

于是我想了一个办法来解决它。

设一个结构体

struct node
{
    int v[maxn];//重边的值
    int cnt;//该重边的数量
    int flag;//是否有重边
    int flag1;
}mark[maxn][maxn];

特意用来处理重边问题;mp[i][j]只要保存的是重边中最小的那条就不影响MST的求值;只要在算SMST的时候把重边考虑进去就行了。

结果……XJB胡写了162行代码,代码太丑,WA了N次,也不知道哪有问题;

/* ***********************************************
Author        :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define LL long long
#define maxn 210
#define N 100000000


int mp[maxn][maxn];
int used[maxn][maxn];
int Max[maxn][maxn];
int vis[maxn],lowc[maxn],pre[maxn];
int m,n;
struct node
{
    int v[maxn];
    int cnt;
    int flag;
    int flag1;
}mark[maxn][maxn];
int Prim()
{

    memset(Max,0,sizeof(Max));
    memset(used,0,sizeof(used));
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=n;i++)
    {
        pre[i]=1;
        lowc[i]=mp[1][i];
    }
    lowc[1]=0;
    vis[1]=1;
    pre[1]= -1;
    int ans=0;
    for(int i=2;i<=n;i++)
    {
        int minc=N,p=-1;
        for(int j=1;j<=n;j++)
            if(!vis[j] && lowc[j]<minc)
            {
                minc=lowc[j];
                p=j;
            }
        if(minc==N)
            return -1;
        ans+=minc;
        vis[p]=1;
        used[p][pre[p]]=used[pre[p]][p]=1;
        for(int j=1;j<=n;j++)
        {
            if(vis[j] && j!=p)
                Max[p][j]=Max[j][p]=max(Max[j][pre[p]],lowc[p]);
            if(!vis[j] && mp[p][j] < lowc[j])
            {
                lowc[j]=mp[p][j];
                pre[j]=p;
            }
        }
    }
    return ans;
}
int f[maxn][maxn];
int smst()
{

    int minc=N,ans=Prim();
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            if(mark[i][j].flag && used[i][j])
            {
                for(int k=0;k<mark[i][j].cnt;k++)
                    if(k!=f[i][j])
                        minc=min(minc,ans+mark[i][j].v[k]-mp[i][j]);
            }
            if(mp[i][j]!=N && !used[i][j])
                minc=min(minc,ans+mp[i][j]-Max[i][j]);
        }
    if(minc==N)
        return -1;
    return minc;
}

int main()
{
    int t,ca=1;
    scan(t);
    while(t--)
    {
        int u,v,w;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            {
                mp[i][j]=mp[j][i]=N;
                f[i][j]=f[j][i]=0;
                mark[i][j].flag=mark[j][i].flag=0;
                mark[i][j].flag1=mark[j][i].flag1=0;
                mark[i][j].cnt=mark[j][i].cnt=1;
            }

        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            if(mark[u][v].flag1)
            {
                mark[u][v].flag=1;
                int k = mark[u][v].cnt++;
      //          printf("k=%d\n",k);
                mark[u][v].v[k]=w;
                if(mp[u][v]>w)
                {
                    f[u][v]=f[v][u]=k;
                    mp[u][v] = mp[v][u] = w;
                }
                continue;
            }
            mp[u][v]=mp[v][u]=w;
            mark[u][v].v[0]=mark[v][u].v[0]=w;
            mark[u][v].flag1=mark[v][u].flag1=1;

        }
   //     printf("f=%d\n",f);
   //     for(int i=0;i<mark[4][5].cnt;i++)
   //         printf("%d\n",mark[4][5].v[i]);
   //     printf("mp=%d\n",mp[4][5]);
        int ans1=Prim();
        printf("Case #%d : ",ca++);
        if(ans1==-1)
        {
            printf("No way\n");
            continue;
        }
  //      printf("ans1=%d\n",ans1);
        int ans2=smst();
        if(ans2==-1)
            printf("No second way\n");
        else
            printf("%d\n",ans2);

    }
    return 0;
}


百度一下,原来重边用kruskal算法是极好的;又学到了。确是是这样,prim算法有点两两枚举点的感觉,与点的关系比较密切,而kruskal则是从边出发;当边有重边,再枚举点已不合适,从边出发非常符合常理。

kruskal求次小生成树和prim算法求 的核心思想应该说是一致的,都是先求出MST,再通过删除MST上的一条边,构造新的生成树;枚举所有的边,得出答案。Prim时间复杂度 O(V*V); kruskal时间复杂度 O(V*E);(自己分析的,应该没错吧0-0)

最终AC代码:

/* ***********************************************
Author        :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define LL long long
#define maxn 222
#define INF 1000000

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

bool cmp(Edge n1,Edge n2)
{
    return n1.w<n2.w;
}

int p[maxn],used[maxn];

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

int main()
{
   // freopen("out.txt","w",stdout);
   int t,n,m,ca=1;
   scan(t);
   while(t--)
   {
        scann(n,m);
        REP(i,0,m)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        sort(edge,edge+m,cmp);
        REPP(i,0,n) p[i]=i;
        int ans=0,cnt=0;
        REP(i,0,m)
        {
            int u=edge[i].u;
            int v=edge[i].v;
            int t1=find(u);
            int t2=find(v);
            if(t1!=t2)
            {
                ans+=edge[i].w;
                p[t1]=t2;
                used[cnt++]=i;
            }
            if(cnt==n-1) break;
        }
        printf("Case #%d : ",ca++);
        if(cnt < n-1)
        {
            printf("No way\n");
            continue;
        }
         /*求次小生成树*/
        if(m==n-1)
        {
            printf("No second way\n");
            continue;
        }
        int ans2=INF;
        //printf("cnt=%d\n",cnt);
        REP(i,0,cnt)
        {
            REPP(j,0,n) p[j]=j;
            int smst=0,ct=0;
            REP(j,0,m)
            {
                if(j==used[i]) //轮流删除MST中的一条边求S_MST
                    continue;
                int u=edge[j].u;
                int v=edge[j].v;
                int t1=find(u);
                int t2=find(v);
                if(t1!=t2)
                {
                    smst += edge[j].w;
                    p[t1]=t2;
                    ct++;

                }
                if(ct==n-1) break;
            }
            if(ct==n-1)
                ans2=min(ans2,smst);
        }
        if(ans2==INF)
            printf("No second way\n");
        else
            printf("%d\n",ans2);
   }
    return 0;
}

你可能感兴趣的:(Uva 10462 Is There A Second Way Left? Kruskal求次小生成树)