次小生成树模板 poj 1679

最小生成树和次小生成树定义:
生成树:图G的生成树包含原图中的全部顶点n,和n-1条边.
最小生成树就是所有生成树中边权和最小的.

2 那么如何求最小生成树呢?
介绍两种基于贪心的算法:

1.prim算法

    设图G =(V,E),其生成树的顶点集合为U。

  ①、把v0放入U。

  ②、在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。

  ③、把②找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行②。

2.kruskal算法
      首先将所有边按边权排序,然后按照边权从小到大依次处理.这里要用到并查集的思想,假设已有点集U,现在正在处理边i->j,如果i,j已在

U中则处理下一条边,否则将i,j加入并查集中.继续处理下一条边,直到有n-1条边为止.

 

3 次小生成树
次小生成树可由最小生成树换一条边得到

算法:
1)先用prim求出最小生成树T,在prim的同时,用一个矩阵max[u][v]记录在树中连接u-v的路径中权值最大的边.
2)枚举所有不在T中的边u-v,加入边u-v,删除权值为max[u][v]的边,不断枚举找到次小生成树.

POJ1679 The Unique MST

题意:给定图,让求它的最小生成树是否唯一。如果唯一的话输出最小生成树的权值和,否则输出Not Unique!

思路:直接求次小生成树就行。


次小生成树的模板:

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
int g[110][110],dist[110],mmax[110][110];
int pre[110];
bool mark[110];
bool connect[110][110];
int mst,mint;
int n,m;
int prim()
{
       int res=0,fa,p,min,i,j;
       memset(mmax,0,sizeof(mmax));
       for(i=1;i<=n;i++)
       {
              dist[i]=g[1][i];
              pre[i]=1;
              mark[i]=false;
       }
       dist[1]=0;
       mark[1]=true;
       for(i=1;i<n;i++)
       {
              p=-1;min=INF;
              for(j=1;j<=n;j++)
              {
                     if(!mark[j]&&dist[j]<min)
                     {
                            p=j;
                            min=dist[j];
                     }
              }
              if(p==-1) return res;
              mark[p]=true;
              res+=dist[p];
              fa=pre[p];
              connect[fa][p]=false;
              connect[p][fa]=false;
              mmax[fa][p]=min;
              for(j=1;j<=n;j++)
                     mmax[j][p]=(mmax[fa][p]>mmax[j][fa])?mmax[fa][p]:mmax[j][fa];
              for(j=1;j<=n;j++)
              {
                     if(!mark[j]&&dist[j]>g[p][j])
                     {
                            dist[j]=g[p][j];
                            pre[j]=p;
                     }
              }
       }
       return res;
}

int main()
{
       int tc;
    //freopen("1.txt","r",stdin);
       scanf("%d",&tc);
       while(tc--)
       {
              scanf("%d %d",&n,&m);
              memset(g,INF,sizeof(g));
              memset(connect,false,sizeof(connect));
              while(m--)
              {
                     int u,v,c;
                     scanf("%d %d %d",&u,&v,&c);
                     g[u][v]=c;
                     g[v][u]=c;
                     connect[u][v]=true;
                     connect[v][u]=true;
              }
              mst=prim();
              int i,j;
              bool flag=false;
              for(i=1;i<=n;i++)
                     for(j=1;j<=n;j++)
                     {
                            if(connect[i][j]==false||g[i][j]==INF)
                                   continue;
                            if(g[i][j]==mmax[i][j])
                            {
                                   flag=true;
                                   break;
                            }
                     }
              if(flag)
                     printf("Not Unique!\n");
              else
                     printf("%d\n",mst);
       }
       return 0;
}

2、Kruskal

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 1010;
const int MAXM = 100010;

int father[MAXN];

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

struct Node
{
    int from;
    int to;
    int w;
    bool vis;
};
Node Edges[MAXM];//存储边信息

bool cmp(Node a, Node b)
{
    return a.w < b.w;
}

//链式前向星
struct Node1
{
    int to;
    int next;
};
Node1 Vertex[MAXN];//边数组,表示结点连向的边

int N,M;
int head[MAXN];     //邻接表头结点位置
int End[MAXN];      //邻接表尾结点位置,方便合并
int Len[MAXN][MAXN];//图中两点之间在最小生成树上路径最长的边

void Kruskal()
{
    int x,y,k = 0;
    int ans = 0;
    
    //初始化邻接表,每个节点初始的时候添加一条指向自己的边,表示结点i各自为一个集合
    memset(head,-1,sizeof(head));   
    memset(End,-1,sizeof(End));
    for(int i = 1; i <= N; i++)
    {
        Vertex[i].to = i;
        Vertex[i].next = head[i];
        End[i] = i;
        head[i] = i;
    }
    
    sort(Edges,Edges+M,cmp);//边按权值排序
    for(int i = 0; i < M; i++)
    {
        if(k == N-1)
            break;
        if(Edges[i].w < 0)
            continue;
        x = find(Edges[i].from);
        y = find(Edges[i].to);
        if(x != y)
        {
            //遍历两个节点所在的集合
            for(int w = head[x]; w != -1; w = Vertex[w].next)
            {
                for(int v = head[y]; v != -1; v = Vertex[v].next)
                {
                    Len[Vertex[w].to][Vertex[v].to] = Len[Vertex[v].to][Vertex[w].to] = Edges[i].w;
                    //当前加入的边一定是加(x,y)边成环后删去的除(x,y)外长度最大的边
                }
            }
            Vertex[End[y]].next = head[x];//合并两个邻接表,表示两点已连边连在一个集合中,最终连成一个最小生成树
            head[x] = head[y];
            End[y] = End[x];
            father[y] = x;
            k++;
            Edges[i].vis = true;
        }
    }
}

int main()
{
    int T,x,y,w;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        for(int i = 1; i <= N; i++)
            father[i] = i;
        memset(Len,0x7f,sizeof(Len));
        for(int i = 0; i < M; i++)
        {
            scanf("%d%d%d",&x,&y,&w);
            Edges[i].from = x;
            Edges[i].to = y;
            Edges[i].w = w;
            Edges[i].vis = false;
        }

        int MST,SecMST;
        Kruskal();
        MST = 0;//最小生成树长度
        for(int i = 0; i < M; i++)
        {
            if(Edges[i].vis)
                MST += Edges[i].w;
        }
        SecMST = 0xfffff0;
        for(int i = 0; i < M; i++)
        {
            if(!Edges[i].vis)//加边,并删去最小生成树上的边
                SecMST = min(SecMST,MST+Edges[i].w - Len[Edges[i].from][Edges[i].to]);
        }
        if(SecMST == MST)
            printf("Not Unique!\n");
        else
            printf("%d\n",MST);
    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值