EOJ_1094_寻找航海路线

#include <iostream>
#include <cstring>
#define INF 0x3f3f3f3f
 
using namespace std;
 
/*Prim算法需要的全局变量*/
bool visited [501];
int cost[501][501];
int pre[501];
int lowc[501];
/*Prim算法需要的全局变量*/
/*次小生成树需要的全局变量*/
bool used[501][501]; //标记边是否被用在最小生成树中
int max_edge[501][501]; //记录两个点之间最长的边的长度
/*次小生成树需要的全局变量*/
 
 
int Prim(int n)
{
    int total = 0, index;
    for(int i=2; i<=n; ++i){
        lowc[i] = cost[1][i];
        if(lowc[i]<INF)
            pre[i] = 1;
    }
    pre[1] = 0;
    visited[1] = true;
    for(int i=2; i<=n; ++i){
        int min_e = INF;
        for(int j=2; j<=n; ++j){
            if(!visited[j] && lowc[j]<min_e){
                min_e = lowc[j];
                index = j;
            }
        }
        total += lowc[index];
        visited[index] = true;
        used[index][pre[index]] = used[pre[index]][index] = true; //标记这条边用过了
        for(int j=2; j<=n; ++j){
            if(!visited[j] && cost[index][j]<lowc[j]){
                lowc[j] = cost[index][j];
                pre[j] = index;
            }
            if(visited[j] && j!=index) //更新最长边
                //这里其实是动态规划,假设2-3-4-5联通,求2-5中的最长边,
                //其实就是等价于2-3-4中的最长边与4-5边中的较大值
                //即max(max_edge(2-3-4),length(4-5))
                max_edge[index][j] = max_edge[j][index] = max(max_edge[pre[index]][j], lowc[index]);
        }
    }
    return total;
}
 
 
int second_st(int n, int MST)
{
    int total = INF;
    /*遍历所有边*/
    for(int i=1; i<=n; ++i)
        for(int j=i+1; j<=n; ++j){
            if(!used[i][j] && cost[i][j]<INF)
                total = min(MST-max_edge[i][j]+cost[i][j], total);
        }
    if(total == INF) //找不到次小生成树
        total = -1;
    return total;
}
 
 
int main()
{
    int N, M, u, v, w, MST, SMT;
    while(cin>>N>>M){
        memset(visited, 0, sizeof(visited));
        memset(max_edge, 0, sizeof(max_edge));
        memset(used, 0, sizeof(used));
        for(int i=1; i<=N; ++i)
            for(int j=1; j<=N; ++j)
                cost[i][j] = INF;
        for(int i=0; i<M; ++i){
            cin>>u>>v>>w;
            cost[u][v] = w;
            cost[v][u] = w;
        }
        MST = Prim(N);
        SMT = second_st(N, MST);
        cout<<MST<<' '<<SMT<<endl;
    }
    return 0;
}

求次小生成树

定义:所有生成树中路径长是第二小的树
可行变换:一棵生成树换一条边仍然为生成树,称为进行了可行变换
临集:换了边的生成树 ∈ \in 原来生成树的临集

  1. 用定义,得到所有的生成树(spanning tree),路径总和排第二的就是次小生成树
  2. 求得最小生成树后枚举n-1条边,循环n-1次,分别去掉第i(i ∈ \in [1,n-1])条边后得到的最小生成树路径,用vector存储,取最小值即可
  3. 根据“次小生成树一定在最小生成树的临集中”定理
  4. 枚举要加入哪条新边(U-V),加入后会出现一条回路,因此删除的边必须是在MST上从u到v的路径上,而且是这条路径的最长边
  5. 因为次小生成树的路径和与最小生成树的路径和之差等于加入的边与删除的边之差,
  6. 即求MST外的一条边与边的端点间最长边长度之差
  7. 再遍历所有MST外的边即可

注意

  1. prim 算法模板是用 lowc[i] = 0 表示点i 被选中
  2. 在动态规划得到max_edge时注意lowc[j]此时为0
  3. 即使在动态规划中解决了lowc[j]==0的问题(用一个tmpLostc暂时存储lowc[j]即可),我也没有成功计算出正确的max_edge,很怪

[参考]
1.https://www.bilibili.com/video/BV1CC4y1a7kD?from=search&seid=17175123924411804815&spm_id_from=333.337.0.010.
2.https://blog.csdn.net/CoolCoolCarrot/article/details/100675150?ops_request_misc=&request_id=&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2alles_rank~default-1-100675150.pc_search_es_clickV2&utm_term=%E5%AF%BB%E6%89%BE%E8%88%AA%E6%B5%B7%E8%B7%AF%E7%BA%BF&spm=1018.2226.3001.4187

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值