[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!\n");
    else printf("%d\n",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\n",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\n",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\n");
    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\n");
        else printf("%d\n",ans2);
    }
    /*for(int i=0;i<tot;i++)
        printf("%d\n",gc[i]);*/
    //printf("%d\n",ans1);
}
}

  

时间: 2024-10-20 14:22:32

[kuangbin带你飞]专题八 生成树 - 次小生成树部分的相关文章

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

A. POJ 1679  The Unique MST 题意:最小生成树是不是唯一的. 思路:参考http://www.cnblogs.com/onlyAzha/p/4793031.html B. HDU 4081  Qin Shi Huang's National Road System 题意:秦始皇想要在城市之间修路.徐福可以用法力帮助他修一条路.秦始皇希望修一条路使得剩下的路需要的花费B最少,而徐福希望这条路所连的两个城市的人口数之和A最大.权衡后他们决定选择A/B的值最大的边来修建. 思

kuangbin带你飞专题一 简单搜索 题解

目录 [kuangbin带你飞]专题一 简单搜索 [kuangbin带你飞]专题一 简单搜索 总结:用时2天半终于把这个专题刷完了 对于最基础的dfs bfs 路径打印 状态转移也有了一点自己些微的理解 其实2天半可以压缩到1天半的 主要是自己太懒了...慢慢加油刷bin神的专题呀 从大二下学期开始学算法 一开始就知道这个专题 一开始对于这个专题里的所有问题感觉都好难啊..就直接放弃了 看lrj的书 现在看到这个专题还挺唏嘘的吧 突然觉得思维和想法也不是很难 果然是那个时候心不静&还是储量不够吗

[kuangbin带你飞]专题十六 KMP &amp; 扩展KMP &amp; Manacher :G - Power Strings POJ - 2406(kmp简单循环节)

[kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher G - Power Strings POJ - 2406 题目: Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of

[kuangbin带你飞]专题十 匹配问题 一般图匹配

过去做的都是二分图匹配 即 同一个集合里的点 互相不联通 但是如果延伸到一般图上去 求一个一般图的最大匹配 就要用带花树来解决 带花树模板 用来处理一个无向图上的最大匹配 看了一会还是不懂  抄了一遍kuangbin的模板熟悉了一下 还有一个一般图最大权匹配 保存下来了VFK菊苣的模板题代码当作板子 http://uoj.ac/submission/16359 但愿以后的比赛永远也遇不到 .. 遇到了也能抄对 .. 抄错了也能过 .. R ural1099 kuangbin模板 #include

[kuangbin带你飞]专题六 最小生成树

学习最小生成树已经有一段时间了 做一些比较简单的题还算得心应手..花了三天的时间做完了kuangbin的专题 写一个题解出来记录一下(虽然几乎都是模板题) 做完的感想:有很多地方都要注意 n == 1 注意double 的精度问题 poj 1251 模板题 大写字母减去'A'+1即是它的编号 #include<stdio.h> #include<string.h> #include<algorithm> #include<map> #include<m

【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵

https://vjudge.net/contest/66989#problem/A 单点修改,区间查询 方法一:线段树 http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139834.html 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<cmath> 6 #

[kuangbin带你飞]专题十 匹配问题 二分图多重匹配

二分图的多重匹配问题不同于普通的最大匹配中的"每个点只能有最多一条边" 而是"每个点连接的边数不超过自己的限定数量" 最大匹配所解决的问题一般是"每个人都有一群想加入的团体 并且一个团体只能收一个人 问有多少人可以加入一个自己喜欢的团体" 而多重匹配是 "每个人都有一群想加入的团体 每个团体可以收给定的人数 问有多少人可以加入一个自己喜欢的团体" 解决这个问题 目前看貌似有三个办法 1 拆点 一个团体可以招x个人 就把它拆成x

【算法系列学习】DP和滚动数组 [kuangbin带你飞]专题十二 基础DP1 A - Max Sum Plus Plus

A - Max Sum Plus Plus 1 https://vjudge.net/contest/68966#problem/A 2 3 http://www.cnblogs.com/kuangbin/archive/2011/08/04/2127085.html 4 5 /* 6 状态dp[i][j]有前j个数,组成i组的和的最大值.决策: 7 第j个数,是在第包含在第i组里面,还是自己独立成组. 8 方程 dp[i][j]=Max(dp[i][j-1]+a[j] , max( dp[i-

[kuangbin带你飞]专题六 最小生成树 G - Arctic Network

G - Arctic Network 题目链接:https://vjudge.net/contest/66965#problem/G 题目: 国防部(DND)希望通过无线网络连接几个北部前哨站.在建立网络时将使用两种不同的通信技术:每个前哨站都有一个无线电收发器,一些前哨站还有一个卫星信道.    任何带卫星频道的两个前哨站都可以通过卫星进行通信,无论其位置如何.否则,两个前哨只有当它们之间的距离不超过D时才可以通过无线电进行通信,这取决于收发器的功率.更高的功率产生更高的D但成本更高.由于采购