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

来源:互联网 发布:淘宝手机拍照技巧集锦 编辑:程序博客网 时间:2024/06/08 01:50

这两天做了三题次小生成树包括上篇博客都是用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 100000000int 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 1000000struct 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;}

0 0
原创粉丝点击