poj 3694Network 双联通分量+lca

本文详细介绍了如何通过Tarjan算法快速计算加边后图中的桥数量,包括DFS遍历和LCA查找的优化,提供了一个简洁高效的解决方案。

输入:
3 2
1 2
2 3
2
1 2
1 3
题意就是先输入n,m 表示一颗树有n个节点,m条无向边。然后又q次操作, 每一次操作 输入i,j 表示在节点i和节点j之间加一条边,并且询问在加边之后的图中存在多少个桥。

在这里先感谢大牛:
http://www.cnblogs.com/scau20110726/archive/2013/06/14/3135095.html
他的博客真的给了我很多的帮助,在我犯了很多很多错误的时候 用很简短很清晰的代码拨正了我的错误思维。膜一发

这题 我一开始是这样写的: 先dfs 跑一发tarjan,把dfn,low 桥 连通块 都跑出来。 然后再 dfs 跑一发st的lca在线询问, 然后对于加边 i,j 我们就从i 向上遍历到 lca,从j 向上遍历到lca,扫描减少的桥的数量。 不知道是不是姿势写的太丑,还是两遍dfs耗时间,反正就是T了。 (现在想想 好像是因为 fa[i] 写的有问题才会T)
然后膜了一下别人的代码, 感觉至少比我的代码少 100 行 TUT,我好菜啊

感觉自己没有学懂tarjan啊,好菜啊,明明一遍dfs就可以做完,因为我们后面lca 用很朴素的想法的话用dfn和low就可以完成了,话不多说 上代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int N = 100010;//点数
const int MAXM = 200010;//边数,因为是无向图,所以这个值要*2

int head[N],dfn[N],low[N],vis[N],fa[N],dcnt,bridge;
int isbri[N];
struct Edge{
    int u,v,next;
    int used;  //判断是否为桥
    int bri;
}edge[2*MAXM];
int tot;
void addedge(int u,int v){
    edge[tot].v=v;
    edge[tot].used=0;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u,int pre){
//  printf("u=%d\n",u);
    vis[u]=1;
    dfn[u]=low[u]=++dcnt;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
//      if(v==pre) continue;
        if(!edge[i].used){
            edge[i].used=edge[i^1].used=1;
            if(!vis[v]){
                fa[v]=u;
                dfs(v,u);
                if( low[u] > low[v] )  low[u] = low[v];
                if(low[v] > dfn[u]){   //桥
                    bridge++;
                    isbri[v]=1;  // 精妙: 只会设置每一条路的终点,反向并不会设置
                }
            }
            else if(vis[u] && low[u]> dfn[v])
                low[u]=dfn[v];
        }
    }
}
void LCA(int u,int v){ //重点来了, 其实LCA完全没必要再扫一遍,借助low 和 dfn 完全够实现lca

    if(dfn[u] < dfn[v]) swap(u,v);  // dfn  即 深度
    while(dfn[u] > dfn[v]){   //因为 u是较深的那个点,先尽量往上移动
        if(isbri[u]) bridge--;
        isbri[u]=0;
        u=fa[u];
    }
    while(dfn[v]>dfn[u]){
        if(isbri[v]) bridge--;
        isbri[v]=0;
        v=fa[v];
    }
    while(u!=v){
        if(isbri[u]) bridge--;
        if(isbri[v]) bridge--;
        isbri[u]=isbri[v]=0;
        u=fa[u];
        v=fa[v];
    }
    //最后 u=v , 这个u就是lca,但我不说
    //讲道理  复杂度有点高啊
}
bool flag[N];
void init(){
    tot=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(isbri,0,sizeof(isbri));
    memset(flag,0,sizeof(flag));
    dcnt = bridge = 0;
}
int main(){
   // freopen("1.txt","r",stdin);
    int n,m,cas=0;
    while(~scanf("%d %d",&n,&m) && (n||m)){
        init();
        printf("Case %d:\n",++cas);
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d %d",&u,&v);
            addedge(u,v);
            addedge(v,u);
            flag[v]=true;
        }
        dfs(1,1);
        int q;
//      printf("bri=%d\n",bridge);
//      for(int i=1;i<=n;i++){
//          printf("%d ",dfn[i]);
//      }
//        cout<<endl;
        scanf("%d",&q);
        while(q--){
            int a,b;
            scanf("%d %d",&a,&b);
            if(bridge==0){
                printf("0\n");
                continue;
            }
            LCA(a,b);
            printf("%d\n",bridge);
        }
    }
    return 0;
}
内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值