【算法每日一练]-图论(保姆级教程篇12 tarjan篇)#POJ3352道路建设 #POJ2553图的底部 #POJ1236校园网络 #缩点

目录

        

POJ3352:道路建设

        思路:

POJ2553:图的底部

       思路:

POJ1236校园网络

       思路:

缩点: 

      思路:


        

        

POJ3352:道路建设

        
由于道路要维修,维修时候来回都不能走,现要在各个景点间建设新道路以便维修时候也能保证任何两个景点之间可以相互到达,求最少的新道路数量
任何一对景点间最多只能在它们之间有一条道路(没有重边)。道路一开始是联通的

输入:
3 3
1 2
2 3
1 3

10 12
1 2
1 3
1 4
2 5
2 6
5 6
3 7
3 8
7 8
4 9
4 10
9 10

        
思路:

先求解边双连通分量,然后缩点,然后通过加边再把新图变成双连通图。

加边原理是这样的:
先统计叶节点个数为k,(k+1)/2就是要建的边数。因为在树中,给叶节点加边一定会产生环

说一下tarjan后的操作 

for(int u=1;u<=n;u++)for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(low[u]!=low[v]) deg[low[u]]++;//遍历新图的边(其实就是旧图的桥)
//有重边也要记录。low[u]就是连通分量号,每个连通分量中只有桥的点才有度}int leaf=0;
//		for(int i=1;i<=n;i++){
//			cout<<i<<' '<<deg[i]<<' '<<low[i]<<'\n';//看详情
//		}for(int i=1;i<=n;i++){//检查每个连通分量号的度(一定不为零)if(deg[i]==1) leaf++;//度是1就是叶子}cout<<(leaf+1)/2<<'\n';

 首先是缩点:low是连通分量号,把度(无向图没有入度出度之分)统计到桥点身上(很像并查集中的缩点到祖宗点身上),注意我们这种缩点的过程肯定会遇到重边。此题中的重边是不能去掉的,否则叶节点会统计错误!!!

然后统计度为1就是叶子就行。

        

对于重边:有时候必须要,有时候不影响,有时候也必须去重。要仔细分析!

#include <bits/stdc++.h>//无向图的桥
using namespace std;
const int maxn=1000+5;
int n,m;
int head[maxn],cnt;
struct node{int to,next;}e[maxn*2];
int low[maxn],dfn[maxn],deg[maxn],num;//deg是度(无向图没有入度和出度之分)void add(int u,int v){ e[++cnt]=(node){v,head[u]};head[u]=cnt;}void tarjan(int u,int fa){dfn[u]=low[u]=++num;//初始化for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(v==fa) continue;//不可以走父子边回去if(!dfn[v]){//没访问过就递归访问tarjan(v,u);low[u]=min(low[u],low[v]);//low是自己或子孙能走回的最小dfn}else{//可以从非父子边回去就要获取dfn值,就是该点能回到的最小dfnlow[u]=min(low[u],dfn[v]);}}
}void init(){memset(head,0,sizeof(head));memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(deg,0,sizeof(deg));cnt=num=0;
}int main(){while(cin>>n>>m){init();int u,v;while(m--){cin>>u>>v;add(u,v);add(v,u);}tarjan(1,0);//求边双连通分量for(int u=1;u<=n;u++)for(int i=head[u];i;i=e[i].next){int v=e[i].to;//遍历新图的边(其实就是旧图的桥)if(low[u]!=low[v]) deg[low[u]]++;
//有重边也要记录。low[u]就是连通分量号,每个连通分量中只有桥的点才有度}int leaf=0;
//		for(int i=1;i<=n;i++){
//			cout<<i<<' '<<deg[i]<<' '<<low[i]<<'\n';//看详情
//		}for(int i=1;i<=n;i++){//检查每个连通分量号的度(一定不为零)if(deg[i]==1) leaf++;//度是1就是叶子}cout<<(leaf+1)/2<<'\n';}	
}

        

        

POJ2553:图的底部

        
有向图中若v可以到的任何一个u,u也可以到v,则v是一个sink点,图的底部是由所有sink点构成的,按顺序输出所有sink点编号,没有sink就输出一个空行

输::
3 3
1 3 2 3 3 1
2 1
1 2
0

思路:

你只需要输出出度为0的连通分量中的所有点编号即可
                

for(int u=1;u<=n;u++)for(int i=head[u];i;i=e[i].next){//对所有边进行判断是不是连接着两个分量int v=e[i].to;if(be[u]!=be[v]){//有重边out[be[u]]++;//缩点}}
int f=1;
for(int i=1;i<=n;i++){if(!out[be[i]]){//输出出度为0的连通分量中的点if(f) f=0;else cout<<" ";//一个数前面有个空格cout<<i; }
}

不同于无向图,有向图的连通分量号我们用一个be数组存起来 

然后对所有边进行判断是不是连接着两个分量,然后对新树中的边统计出度,输出出度为0的连通分量中的点

#include <bits/stdc++.h>
using namespace std;
const int maxn=5050;
bool ins[maxn];//标记是否在栈中
int n,m;
int head[maxn],be[maxn],out[maxn];//be是属于哪个连通分量,out是缩点的出度
int low[maxn],dfn[maxn],num,id,cnt;
stack <int> s;
struct node{int to,next;}e[maxn*2];void add(int u,int v){ e[++cnt]=(node){v,head[u]};head[u]=cnt;}void tarjan(int u){dfn[u]=low[u]=++num;//dfn访问序号,low是能走回到的最早的dfnins[u]=1;s.push(u);//第一次访问节点时候入栈for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(!dfn[v]){//没访问过就递归访问tarjan(v);low[u]=min(low[u],low[v]);//获取孩子的最小的low值   }else if(ins[v]){//已经访问过且在栈中获取dfn号low[u]=min(low[u],dfn[v]);}}if(low[u]==dfn[u]){//low[u]==dfn[u]时,则从栈中不断弹出节点,直到x出栈停止。弹出的节点就是同一个连通分量的int v;do{//一定要先执行再判断v=s.top();s.pop();be[v]=id;//把这些弹出的点标记同一个id号(连通分量号)ins[v]=0;}while(v!=u);//直到是自己为止id++;}
}void init(){memset(head,0,sizeof(head));memset(low,0,sizeof(low));memset(ins,0,sizeof(ins));memset(dfn,0,sizeof(dfn));memset(out,0,sizeof(out));memset(be,0,sizeof(be));cnt=num=0;id=1;
}int main(){while((cin>>n)&&n){//点数cin>>m;//边数init();int u,v;while(m--){cin>>u>>v;add(u,v);}for(int i=1;i<=n;i++){if(!dfn[i]) tarjan(i);//有向图}for(int u=1;u<=n;u++)for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(be[u]!=be[v]){//有重边out[be[u]]++;//缩点}}int f=1;for(int i=1;i<=n;i++){if(!out[be[i]]){//输出出度为0的连通分量中的点if(f) f=0;else cout<<" ";//(输出格式罢了,不用在乎这里)cout<<i; }}}	
}

        

        

POJ1236校园网络

        
每所学校都有一份发学校名单。计算至少先发给多少个学校才能使软件传到所有学校(任务1),计算至少增加多少扩展才能将软件发给任意学校结果都能传到所有学校(扩展就是将新成员引入一所学校的接收者名单)
5
2 4 3 0
4 5 0
0
0
1 0

        

思路:

        
任务1:每一个入度为0的连通分量都必须收到一个软件,计算个数。
任务2:每个连通分量必须既有入度也有出度,即入度为0的连通分量必须扩展一下,出度为0的连通分量必须也扩展一下(入度和出度对接,输出max就行)

#include <bits/stdc++.h>//有向图的强连通分量
using namespace std;
const int maxn=5050;
bool ins[maxn];
int n,m,cnt;
int head[maxn],be[maxn],in[maxn],out[maxn];//be是属于哪个连通分量  in,out是每个连通分量的入度和出度
int low[maxn],dfn[maxn],num,id;
stack <int> s;
struct node{int to,next;}e[maxn*2];void add(int u,int v){ e[++cnt]=(node){v,head[u]};head[u]=cnt;}void tarjan(int u){dfn[u]=low[u]=++num;//dfn访问序号,low是能走回到的最早的dfnins[u]=1;s.push(u);//第一次访问节点时候入栈for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(!dfn[v]){//没访问过就递归访问tarjan(v);low[u]=min(low[u],low[v]);//获取孩子的最小的low值   }else if(ins[v]){//已经访问过且在栈中获取dfn号low[u]=min(low[u],dfn[v]);}}if(low[u]==dfn[u]){//low[u]==dfn[u]时,则从栈中不断弹出节点,直到x出栈停止。弹出的节点就是同一个连通分量的int v;id++;do{//一定要先执行再判断v=s.top();s.pop();be[v]=id;//把这些弹出的点标记同一个id号(连通分量号)ins[v]=0;}while(v!=u);//直到是自己为止}
}int main(){cin>>n;int v;//n为学校数量for(int i=1;i<=n;i++){while(cin>>v&&v)add(i,v);//表示接收i的v学校,以0结尾}for(int i=1;i<=n;i++){if(!dfn[i]) tarjan(i);}for(int u=1;u<=n;u++)for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(be[u]!=be[v]){//有重边,可以输出一下in[be[v]]++;out[be[u]]++;//统计入度和出度,来缩点}}if(id==1){//一共只要一个连通分量的话要特判cout<<1<<'\n';cout<<0<<'\n';return 0;}int ans1=0,ans2=0;//for(int i=1;i<=n;i++)cout<<i<<' '<<be[i]<<'\n';for(int i=1;i<=id;i++){//	cout<<i<<" in"<<' '<<in[i]<<" , "<<"out"<<' '<<out[i]<<'\n';if(!in[i]) ans1++;if(!out[i]) ans2++;}cout<<ans1<<'\n';cout<<max(ans1,ans2)<<'\n';	
}

        

        

        

缩点: 

        

         

思路:

有向图中的强连通分量中的所有权值一定要全部加上,所以缩点建出新的DAG图,然后转化成了每个点走一次求最大点权值和
设置dp[v]表示到v点的最大权值和。 dp[v]=max(dp[u])即可,也就是要先求dp[u]再求dp[v],topo排序求一边就行了。完了!
        

	if(low[u]==dfn[u]){//low[u]==dfn[u]时,则从栈中不断弹出节点,直到x出栈停止。弹出的节点就是同一个连通分量的int v;	do{//一定要先执行再判断v=s.top();s.pop();be[v]=u;//把这些弹出的点标记同一个id号(连通分量号)ins[v]=0;if(u==v)break;//自己不要和自己加p[u]+=p[v];}while(v!=u);//直到是自己为止}

首先是缩点操作,要把该连通分量中点的权值加给连通分量点自己(类似无向图的桥点), 

        

for (int i=1;i<=m;i++)//遍历每个边{int u=be[e[i].from],v=be[e[i].to];//from是起点,to是终点if (u!=v)//不同的分量号点间进行建边,有重边也不影响topo结果{newe[++tt]=(node){v,hh[u],u};hh[u]=tt;in[v]++;//建新边过程,相当于add功能}}

然后是给新DAG图建边,以便后面topo。

        

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+15;
int n,m,tot,head[maxn],tt,hh[maxn],p[maxn];//p是每个点的权值,head和tot和e是原图的,hh和tt和newe是新图的
int num,low[maxn],dfn[maxn],ins[maxn],be[maxn];//be是每个所属的连通分量号
int in[maxn],dp[maxn];
stack<int>s;
struct node{int to,next,from;}e[maxn*10],newe[maxn*10];void add(int u,int v){e[++tot]=(node){v,head[u],u};head[u]=tot;}void tarjan(int u){dfn[u]=low[u]=++num;//dfn访问序号,low使能回溯到的最早的dfnins[u]=1;s.push(u);//第一次访问节点时候入栈for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(!dfn[v]){//没访问过就递归访问tarjan(v);low[u]=min(low[u],low[v]);//获取孩子的最小的low值   }else if(ins[v]){//已经访问过且在栈中获取dfn号low[u]=min(low[u],dfn[v]);}}if(low[u]==dfn[u]){//low[u]==dfn[u]时,则从栈中不断弹出节点,直到x出栈停止。弹出的节点就是同一个连通分量的int v;	do{//一定要先执行再判断v=s.top();s.pop();be[v]=u;//把这些弹出的点标记同一个id号(连通分量号)ins[v]=0;if(u==v)break;//自己不要和自己加p[u]+=p[v];}while(v!=u);//直到是自己为止}
}int topo()
{queue <int> q;int tot=0;for (int i=1;i<=n;i++){if(be[i]==i&&!in[i]){q.push(i);dp[i]=p[i];}}while (!q.empty()){int u=q.front();q.pop();for (int i=hh[u];i;i=newe[i].next){int v=newe[i].to;dp[v]=max(dp[v],dp[u]+p[v]);//要最大的起点嘛in[v]--;if (in[v]==0) q.push(v);}}int ans=0;for (int i=1;i<=n;i++)ans=max(ans,dp[i]);return ans;
}
int main()
{scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%d",&p[i]);//权值for (int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);add(u,v);}for (int i=1;i<=n;i++)if (!dfn[i]) tarjan(i);for (int i=1;i<=m;i++){int u=be[e[i].from],v=be[e[i].to];//from是起点,to是终点if (u!=v)//不同的分量号点间进行建边,有重边也不影响topo结果{newe[++tt]=(node){v,hh[u],u};hh[u]=tt;in[v]++;//建新边过程,相当于add功能}}printf("%d",topo());
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/193384.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MDK提示:在多字节的目标代码中,没有此Unicode 字符可以映射到的字符

MDK警告提示在多字节的目标代码中&#xff0c;没有此Unicode 字符可以映射到的字符 警告提示&#xff1a; 在写MDK的工程代码时&#xff0c;发现代码中引入的头文件前方出现一些红色的叉叉&#xff0c;但是编译工程并不报错&#xff0c;功能也能正常执行的&#xff0c;只是提…

JS利用时间戳倒计时案例

我们在逛某宝&#xff0c;或者逛某东时&#xff0c;我们时常看到一个倒计时&#xff0c;时间一到就开抢&#xff0c;这个倒计时是如何做的呢&#xff1f;让我为大家介绍一下。 理性分析一下&#xff1a; 1.用将来时间减去现在时间就是剩余的时间 2.核心&#xff1a;使用将来的时…

C指针介绍(1)

文章目录 每日一言指针的简单介绍内存和地址指针在内存中的存储指针的定义和声明泛型指针 指针的关系运算算数运算关系运算 结语 每日一言 ⭐「 一声梧叶一声秋&#xff0c;一点芭蕉一点愁&#xff0c;三更归梦三更后。 」–水仙子夜雨-徐再思 指针的简单介绍 C语言指针是C语…

人工智能轨道交通行业周刊-第67期(2023.11.27-12.3)

本期关键词&#xff1a;列车巡检机器人、城轨智慧管控、制动梁、断路器、AICC大会、Qwen-72B 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro…

算法工程师面试八股(搜广推方向)

文章目录 机器学习线性和逻辑回归模型逻辑回归二分类和多分类的损失函数二分类为什么用交叉熵损失而不用MSE损失&#xff1f;偏差与方差Layer Normalization 和 Batch NormalizationSVM数据不均衡特征选择排序模型树模型进行特征工程的原因GBDTLR和GBDTRF和GBDTXGBoost二阶泰勒…

React使报错不再白屏

如果代码中出现问题导致报错&#xff0c;通常会使页面报错&#xff0c;导致白屏 function Head() {// 此时模拟报错导致的白屏return <div>Head --- {content}</div> } export default () > {return (<><div>下面是标题</div><Head />…

若依框架分页

文章目录 一、分页功能解析1.前端代码分析2.后端代码分析3. LIMIT含义 二、自定义MyPage,多态获取total1.定义MyPage类和对应的调用方法 一、分页功能解析 1.前端代码分析 页面代码 封装的api请求 接口请求 2.后端代码分析 controller代码 - startPage() getDataTable(…

yolo.txt格式与voc格式互转,超详细易上手

众所周知,yolo训练所需的标签文件类型是.txt的,但我们平时使用标注软件(labelimage等)标注得到的标签文件是.xml类型的,故此xml2txt之间的转换就至关重要了,这点大家不可能想不到,但是网上的文章提供的代码大多数都是冗余,或者难看,难以上手,故此作者打算提供一个相对…

Sharding-Jdbc(3):Sharding-Jdbc分表

1 分表分库 LogicTable 数据分片的逻辑表&#xff0c;对于水平拆分的数据库(表)&#xff0c;同一类表的总称。 订单信息表拆分为2张表,分别是t_order_0、t_order_1&#xff0c;他们的逻辑表名为t_order。 ActualTable 在分片的数据库中真实存在的物理表。即上个示例中的t_…

怎样使用rtsp,rtmp摄像头低延时参于Web视频会议互动直播

业务系统中有大量的rtsp&#xff0c;rtmp等监控直播设备&#xff0c;原大部分都是单一业务监控直播之类&#xff0c;目前很多业务需要会议互动&#xff0c;需要监控参会&#xff0c;提出需摄像头拉流参会的需求&#xff0c;由于rtmp&#xff0c;rtsp原生不支持web播放&#xff…

vue3-在自定义hooks使用useRouter 报错问题

文章目录 前言一、报错分析报错的Vue warn截图&#xff1a;查看文档 二、那么在hook要怎么引入路由呢&#xff1f; 前言 记录在vue3项目中&#xff0c;hook使用useRouter 报错问题 一、报错分析 报错的Vue warn截图&#xff1a; 警告 inject() can only be used inside setup…

【LeetCode刷题笔记】103. 二叉树的锯齿形层序遍历

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

数据链路层之广域网、PPP协议、HDLC协议

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

【C语言之 CJson】学CJson看这一篇就够了

文章目录 前言一、下载CJson二、创建一个json2.1 创建json对象cJSON类型详解 2.2 创建键值对2.3 添加嵌套的 JSON 对象2.4 添加数组创建数组添加元素到数组添加数组到obj 2.5 将 JSON 对象转为字符串2.6 释放内存2.7 示例代码 三、解析json3.1 解析json root3.2 把一个key解析出…

springboot足球社区管理系统

springboot足球社区管理系统 成品项目已经更新&#xff01;同学们可以打开链接查看&#xff01;需要定做的及时联系我&#xff01;专业团队定做&#xff01;全程包售后&#xff01; 2000套项目视频链接&#xff1a;https://pan.baidu.com/s/1N4L3zMQ9nNm8nvEVfIR2pg?pwdekj…

计算机导论——第39章 文件和目录

除了虚拟化CPU和内存&#xff0c;另外一个是持久存储&#xff0c;永久存储信息。持久存储设备与内存不同&#xff0c;内存在断电时内容会丢失&#xff0c;而持久存储设备会保持这些数据不变。 1. 文件和目录 文件就是一个线性字节数组&#xff0c;每个字节都可以读取或者写入…

Bean的加载方式

Bean的加载方式 文章目录 Bean的加载方式bean的xml方式声明bean的加载方式二&#xff1a;XML注解当时声明beanbean的加载方式三&#xff1a;注解方式声明配置类bean加载方式扩展——FactoryBean bean的xml方式声明 <?xml version"1.0" encoding"UTF-8"…

图论|并查集理论基础 1971. 寻找图中是否存在路径

什么是并查集 并查集是一种数据结构&#xff0c;用于处理一些不交集的合并及查询问题。它支持两种操作&#xff1a; 查找&#xff08;Find&#xff09;&#xff1a;确定某个元素属于哪个子集。它可以用来判断两个元素是否属于同一个子集。 合并&#xff08;Union&#xff09;&…

windows 你的电脑不能投影到其他屏幕,请尝试重新安装驱动程序

注意 千万不要去下载什么驱动精灵&#xff0c;太垃圾不好用还一堆附带的软件。按以下步骤进行解决&#xff1a; 解决方法 可能是显卡驱动的问题&#xff0c;我的笔记本按照如下步骤重启一下驱动后解决了&#xff0c;步骤如下: 右键点击桌面的开始菜单&#xff0c;选择”设备…

javaee实验:MVC 框架技术应用——URL 映射及方法参数的使用

目录 urlmvc框架mvc框架的设计mvc流程 实验目的实验内容实验过程创建项目创建项目结构编写代码简单测试一下 url 和 Hypertext 以及 HTTP 一样&#xff0c;URL 是 Web 中的一个核心概念。它是浏览器用来检索 web 上公布的任何资源的机制 URL 代表着是统一资源定位符&#xff…