[kuangbin带你飞]专题八 生成树

百度了好多自学到了次小生成树 理解后其实也很简单 

求最小生成树的办法目前遇到了两种 

1 prim 记录下两点之间连线中的最长段 F[i][k] 之后枚举两点 若两点之间存在没有在最小生成树中的边 那么尝试加入它 然后为了不成环 要在环中去除一条边 为了达到"次小"的效果 减去最长的 即F[i][k] 求一下此时的数值 不断更新次小值

2 kru 记录下被加入到最小生成树中的线段 然后进行n-1次枚举 每次都跳过一条被记录的边 求一次kru 得到的值为-1或者一个可能成为次小的值 不断更新 判断有无次小生成树 得到次小值

poj 1679 求一个图中是否存在唯一的最小生成树 在最小生成树的专题中采用的办法是在加入最后一条可行边的时候看相同权值的边有没有另外的选择 如果有 就不唯一 如果使用次小生成树的办法来解 可以求一次次小生成树 如果次小值=最小值 不唯一 反之则反

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<math.h>
#include<queue>
#include<iostream>
using namespace std;
#define MAXN 105
#define MAXM 10050
int dis[MAXN];
int ta[MAXN][MAXN];
int pre[MAXN];
bool use[MAXN][MAXN];
bool vis[MAXN];
int F[MAXN][MAXN];
int ans1,ans2;
int n,m;
int prim(){
int ans=0;
vis[1]=false;
for(int i=1;i<=n;i++)pre[i]=1;
pre[1]=-1;
for(int i=2;i<=n;i++)
{
    int minn=999999999;
    int p=-1;
    for(int k=2;k<=n;k++)
    {
        if(vis[k]&&dis[k]<minn)
        {
            p=k;
            minn=dis[k];
        }
    }
    if(minn==999999999)
        return -1;
    vis[p]=false;
    ans+=dis[p];
    use[p][pre[p]]=use[pre[p]][p]=false;
    for(int k=1;k<=n;k++)
    {
        if(!vis[k])
        F[p][k]=F[k][p]=max(dis[p],F[k][pre[p]]);  /// dis[p] 当前树到p点的距离 F[k][pre[p]] k点到曾直接松弛过p点的点的uli[略雾]
        if(vis[k])
        {
            if(dis[k]>ta[p][k])
            {
                dis[k]=ta[p][k];
                pre[k]=p;
            }
        }
    }
}
    return ans;
}
int second()
{
    int ans=999999999;
    for(int i=1;i<=n;i++)
    for(int k=i+1;k<=n;k++)
    {
        if(use[i][k])
        if(ans>ans1-F[i][k]+ta[i][k])
            ans=ans1-F[i][k]+ta[i][k];
    }
    return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
    scanf("%d%d",&n,&m);
    memset(use,false,sizeof(use));
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
        ta[i][k]=999999999;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        ta[u][v]=ta[v][u]=w;
        use[u][v]=use[v][u]=true;

    }
    for(int i=2;i<=n;i++)
        dis[i]=ta[1][i];
    dis[1]=0;
    memset(vis,true,sizeof(vis));
    memset(F,0,sizeof(F));
    ans1=prim();
    ans2=second();
    if(ans1==ans2)
        printf("Not Unique!
");
    else printf("%d
",ans1);
}
}

hdu 4081 题意简直...秦始皇想让n个郡县都可以相互到达 又想让道路尽可能的短 徐福说他可以让一条路刷的一下建成 不耗费人力(所耗人力等于道路两端郡县的人数) 然后两人分歧 最后决定弄出来一个折中的办法 B为除了徐福建造的路的所有路的长度 A为徐福建造的路省下的人力 使A/B最大 求最大值 

我们求出最小生成树 如果徐福建造的路在最小生成树中 枚举这些路 更新比值

如果徐福建造的路不在呢?次小生成树的思想 加入徐福建造的路 然后减去F[i][k] 更新比值

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<math.h>
using namespace std;
int tot;
double ans;
struct node
{
    double x,y,p;
};
node point[1050];
int n;
double disz(int p1,int p2)
{
    return sqrt((point[p1].x-point[p2].x)*(point[p1].x-point[p2].x)+(point[p1].y-point[p2].y)*(point[p1].y-point[p2].y));
}
double dis[1050];
double cost[1050][1050];
int pre[1050];
bool use[1050][1050];
double F[1050][1050];
bool vis[1050];
double prim()
{
    double res=0;
    memset(vis,true,sizeof(vis));
    vis[1]=false;
    for(int i=2;i<=n;i++)pre[i]=1;
    pre[1]=0;
    for(int i=1;i<=n-1;i++)
    {
        int p=-1;
        double minn=9999999999;
        for(int k=1;k<=n;k++)
        {
            if(vis[k])
            if(dis[k]<minn)
            {
                p=k;
                minn=dis[k];
            }
        }
        if(p==-1)
            return -1;
        vis[p]=false;
        res+=dis[p];
        use[p][pre[p]]=use[pre[p]][p]=true;
        for(int k=1;k<=n;k++)
        {
            if(!vis[k]&&p!=k)
                F[p][k]=F[k][p]=max(dis[p],F[k][pre[p]]);
            if(vis[k])
            if(dis[k]>cost[p][k])
            {
                dis[k]=cost[p][k];
                pre[k]=p;
            }
        }
    }
    return res;
}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
    tot=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&point[i].x,&point[i].y,&point[i].p);
    for(int i=1;i<=n;i++)
    for(int k=i;k<=n;k++)
    {
        cost[k][i]=cost[i][k]=disz(i,k);
    }
    for(int i=2;i<=n;i++)
    dis[i]=cost[1][i];
    dis[1]=0;
    memset(F,0,sizeof(F));
    memset(use,false,sizeof(use));
    double ans=prim();
    ///枚举边
    double pri=0;
    for(int i=1;i<=n;i++)
    {
        for(int k=i+1;k<=n;k++)
        {
            if(use[i][k])
            {
                double peo=(point[i].p+point[k].p);
                double w = cost[i][k];
                double ww=ans-w;
                double bz=peo/ww;
                if(bz>pri)
                    pri=bz;
            }
            else
            {
                double peo=(point[i].p+point[k].p);
                double w = ans-F[i][k];
                double bz=peo/w;
                if(bz>pri)
                    pri=bz;
            }
        }
    }
    printf("%.2f
",pri);
}
}

uva 10600 题意就是让求最小生成树值和次小生成树值

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<math.h>
using namespace std;
int n;
int m;
int ans1,ans2;
int cost[105][105];
int dis[105];
bool vis[105];
int F[105][105];
bool use[105][105];
int pre[105];
int prim()
{
    memset(vis,true,sizeof(vis));
    for(int i=1;i<=n;i++)pre[i]=1;
    memset(F,0,sizeof(F));
    vis[1]=false;
    int res=0;
    for(int i=1;i<=n-1;i++)
    {
        int p=-1;
        int minn=999999999;
        for(int j=1;j<=n;j++)
        {
            if(vis[j])
            {
                if(minn>dis[j])
                {
                    minn=dis[j];
                    p=j;
                }
            }
        }
        if(minn==999999999)
            return -1;
        vis[p]=false;
        res+=minn;
        use[p][pre[p]]=use[pre[p]][p]=false;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&j!=p)F[j][p]=F[p][j]=max(F[j][pre[p]],dis[p]); ///因为没有加j!=p 强行wa了几次
            if(vis[j]&&dis[j]>cost[j][p])
            {
                dis[j]=cost[j][p];
                pre[j]=p;
            }
        }
    }
    return res;
}
void second()
{
    ans2=999999999;
    for(int i=1;i<=n;i++)
    {
        for(int k=1;k<=n;k++)
        {
            if(i!=k&&use[i][k]==true&&pre[i]!=k&&pre[k]!=i)
            {
                if(ans2>ans1-F[i][k]+cost[i][k])
                {
                    ans2=ans1-F[i][k]+cost[i][k];
                }
            }
        }
    }
    return ;

}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    for(int k=1;k<=n;k++)
    {
        if(i==k)
            cost[i][k]=0;
        else cost[i][k]=999999999;
    }
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        cost[u][v]=cost[v][u]=min(w,cost[u][v]);
        use[u][v]=use[v][u]=true;
    }
    dis[1]=0;
    for(int i=2;i<=n;i++)
        dis[i]=cost[1][i];
    ans1=prim();
    second();
    printf("%d %d
",ans1,ans2);
}
}

uva 10462 一个图中有没有最小生成树和次小生成树 输出回答或次小值 

难点在于 两个点之间的边并不唯一 在这里想到了用kru来计算最小生成树 即开头提到的第二种办法

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
using namespace std;
int n,m;
struct node
{
    int u,v,w;
};
int fa[105];
node a[205];
void init(){
for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x)
{
    if(fa[x]==x)
        return x;
    return fa[x]=find(fa[x]);
}
void un(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx==fy)
        return ;
    fa[fx]=fy;
    return ;
}
int cmp(node a,node b)
{
    return a.w<b.w;
}
int gc[205];
int tot;
int kru(){
tot=0;
init();
int res=0;
int cnt=0;
if(cnt==n-1)
    return res;
for(int i=0;i<m;i++)
{
    int u=a[i].u;
    int v=a[i].v;
    int w=a[i].w;
    if(find(u)!=find(v))
    {
        gc[tot++]=i;
        un(u,v);
        cnt++;
        res+=w;
    }
    if(cnt==n-1)
        return res;
}
return -1;
}
int kru2(int e)
{
    init();
    int cnt=0;
    int res=0;
    if(cnt==n-1)
        return res;
    for(int i=0;i<m;i++)
    {
        int u=a[i].u;
        int v=a[i].v;
        int w=a[i].w;
        if(i==gc[e])
            continue;
        if(find(u)!=find(v))
        {
            un(u,v);
            cnt++;
            res+=w;
        }
        if(cnt==n-1)
            return res;
    }
    return -1;
}
int main(){
int t;
int tt=0;
scanf("%d",&t);
while(t--)
{
    tt++;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        a[i].u=u;
        a[i].v=v;
        a[i].w=w;
    }
    sort(a,a+m,cmp);
    int ans1,ans2;
    ans1=kru();
    printf("Case #%d : ",tt);
    if(ans1==-1)
        printf("No way
");
    else
    {
        ans2=999999999;
        bool cunzai=false;
        for(int i=0;i<tot;i++)
        {
            int ans3=kru2(i);
            if(ans3!=-1)
            {
                cunzai=true;
                ans2=min(ans2,ans3);
            }
        }
        if(!cunzai)
            printf("No second way
");
        else printf("%d
",ans2);
    }
    /*for(int i=0;i<tot;i++)
        printf("%d
",gc[i]);*/
    //printf("%d
",ans1);
}
}

  

原文地址:https://www.cnblogs.com/rayrayrainrain/p/5456997.html