【算法每日一练]-图论(保姆级教程 篇2(topo排序,并查集,逆元))#topo排序 #最大食物链 #游走 #村村通

今天讲topo排序

目录

题目:topo排序

思路:

题目:最大食物链

解法一:

解法二: 记忆化

题目:村村通

思路:


         

前言:topo排序专门处理DAG(有向无环图)

题目: topo排序

:你有n本书(1~n),阅读第i本数前你要先读Ci本书,现在你要阅读第一本书,问需要阅读那些书?(答案不唯一)

       

思路:

     

看到这样想遍历下一个节点就需要把所有前置都先遍历过的特点,topo就行了。

先把没有前置的书看一下,然后把后置书的前置书数减一,然后看下一个能看的书。

      

主要就是标记维护一个in数组(存入每个点的前置书数)

      

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
vector<int>ve[N];
vector<int>ans;
int n,in[N],vis[N];
void dfs(){//dfs把阅读1书的所有前置书都遍历标记出来,标记出一种方案即可(因为topo序不唯一)
	queue<int>q;
	q.push(1);
	while(!q.empty()){
		int cur=q.front();q.pop();
		for(int i=0,sz=ve[cur].size();i<sz;i++){
			int u=ve[cur][i];
			if(!vis[u])q.push(u);vis[u]=1;
		}
	}
}
void topo(){//正序topo排序
	queue<int>q2;
	for(int i=1;i<=n;i++){//先找出入度为0的点。    当然你完全可以写q.push(1),而我们这里只是为了提供一个topo模板
		if(in[i]==0)q2.push(i),ans.push_back(i);
	}
	while(!q2.empty()){
		int cur=q2.front();q2.pop();
		for(int i=0,sz=ve[cur].size();i<sz;i++){
			int u=ve[cur][i];in[u]--;//去掉一个点就减掉相邻点的入度
			if(in[u]==0)q2.push(u),ans.push_back(u);//入队的点都是等待去掉的点
		}
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){//输入每本书需要的前置书Ci
		int cnt,u;cin>>cnt;
		for(int j=1;j<=cnt;j++){
			cin>>u;//每本前置书编号
			ve[i].push_back(u);in[u]++;
		}
	}
	dfs();//标记1的前置书
	topo();//topo排序遍历出需要的书,我们倒序输出即可
	for(int i=ans.size()-1;i>=0;i--){
		if(vis[ans[i]]) cout<<ans[i]<<' ';
	}
}

           

            

题目:最大食物链

         

解法一:

      

我们标记f[i]是被f[x]捕食的点对应的类食物链数

不难得出: f[x]=∑(f[i])   
首先从生产者开始,每去掉一个被捕食的点,那么相邻捕食者就要加上去掉点的类食物链数,但是我们还需要找到出度为0的消费者。
所以这道题,我们要同时记录入度,还有出度(其实单纯的topo排序就用不上出度,记录出度是为了找食物链结尾的个数)

      

#include<bits/stdc++.h> 
using namespace std;
const int MOD=80112002,M=500005,N=5005;
vector <int>v[N];
queue<int> q;
int n,m,ans,f[N],in[N],out[N];//我们需要标记每个点的入度和出度,f为以该点结尾的类食物链数
int main(){
	cin>>n>>m;int x,y;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		v[x].push_back(y);//y吃x,x指向y
		out[x]++;in[y]++;
	}
	for(int i=1;i<=n;i++){//找到所有没有入度的点为起点
		if(in[i]==0){q.push(i);f[i]=1;}
	}
	while(q.size()){//进行拓扑排序
		int cur=q.front();q.pop();
		for(int i=0,sz=v[cur].size();i<sz;i++){
			int t=v[cur][i];
			f[t]=(f[t]+f[cur])%MOD;in[t]--;//去掉cur点的话,就要把f[cur]加到捕食它的点上
			if(in[t]==0) q.push(t);
		}		
	}
	for(int i=1;i<=n;i++){
		if(out[i]==0)ans=(ans+f[i])%MOD;//出度为0的点的f是我们要的真正食物链数
	}
	cout<<ans;
	return 0;
}

           

解法二: 记忆化


#include <bits/stdc++.h>    //记忆化搜索
using namespace std;
const int N=1e5+5;
int n,m,ans,tot,in[N],out[N],f[N];
vector<int>ve[N];
int dfs(int x){//就是从每个生产者开始,看看能到多少个最终消费者,然后记忆化,最终计算所有生产者就是答案
	if(f[x])return f[x];
	if(!out[x])return 1;
	for(int i=0,sz=ve[x].size();i<sz;i++){
		int v=ve[x][i];
		f[x]+=dfs(v);
	}
	return f[x];
}
int main(){
	cin>>n>>m;int u,v,w;
	while(m--){
		cin>>u>>v;
		ve[u].push_back(v);
		out[u]++;in[v]++;
	}
	for(int i=1;i<=n;i++){
		if(in[i]==0&&out[i])
		ans+=dfs(i);
	}
	cout<<ans;
}

         

        

题目:游走

         

思路: 

       

给一个DAG(有向无环图),求所走路径长度的期望呗!也就是:所有路径长度总和/所有路径个数(因为每条路径概率都一样嘛)

        

明明是DAG图,topo一下完事了

我们设置g[i]表示以i为终点的路径数,f[i]表示i为终点的长度和

   
topo:从点j到一个点i,则g[i]+=g[j],f[i]+=f[j]+g[i](因为啊,从j到i的每个路径长度都只增加1就行,一共增加了g[i])

        
最后就是求(L/S)MOD,也就是L*(S^(MOD-2))MOD即可(逆元小知识)

        

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
const ll MOD=998244353;
int n,m;
ll L,S,f[MAXN],g[MAXN];//g[i]表示以i为终点的路径数,f[i]表示i为终点的长度和
vector<int> edge[MAXN];
int in[MAXN],vis[MAXN];
void topo(void)//模板
{
	queue<int> q;
	for(int i=1;i<=n;i++)
    if(!in[i]) q.push(i);
    while(!q.empty())
    {
    	const int u=q.front();
    	q.pop();
    	if(vis[u]) continue; vis[u]=true;//没有环,所以这句话可以不要
    	for(auto v:edge[u])
    	{
    		in[v]--;
    		if(!in[v])	q.push(v);
    		f[v]=(f[v]+f[u]+g[u])%MOD;
    		g[v]=(g[v]+g[u])%MOD;
		}
	}
}

ll qpow(ll base,ll k)//快速幂求逆元
{
	ll res=1;
	while(k)
	{
		if(k&1)	res=res*base%MOD;
		base=base*base%MOD;
		k>>=1;
	}
	return res;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v;cin>>u>>v;
		edge[u].push_back(v);
		in[v]++;
	}
	for(int i=1;i<=n;i++)	g[i]=1;//初始化:每个点的路径数初始化为1
	topo();
	for(int i=1;i<=n;i++)	L=(L+f[i])%MOD;//获取最终的总长度
	for(int i=1;i<=n;i++)	S=(S+g[i])%MOD;//获取最终的路径个数
	cout<<(L*qpow(S,MOD-2))%MOD<<endl;//求L/S的结果,即L*S的逆元,即L*S^(MOD-2)
	return 0;
}

提一嘴: 

                    
为什么要引入逆元呢?
因为(a+b)%MOD=(a%MOD+b%MOD)%MOD
(a-b)%MOD=(a%MOD-b%MOD)%MOD
(a*b)%MOD=(a%MOD*b%MOD)%MOD  

            
但是除法不满足,我们要求(a/b)%p=1等价于(a*x)%p=1,这个x就是b的乘法逆元(可以理解成x为1/b),也就是(b*x)%p=1

        
再引入费马小定理:假如a和p互质,那么a^(p-1)=1(%p),故a*a^(p-2)=1(%p),故a的逆元x=a^(p-2)%p
因此:(x/y)%p等价于x*y^(p-2)%p,注意每乘一次就要去一次模
     

        

                 

题目:村村通

 

            

思路:

       

并查集步骤:
1,初始化每个点的父亲为自身
2,查找每两个元素所在的集合,找到两个祖宗后返回任意一个并路径压缩(修改中间点的父亲编号为祖宗编号)
3,最后查找有几个祖宗即可(因为有相同的祖宗,就说明祖宗可以到的所有点,也就是它们都是互通的)
      

#include <bits/stdc++.h>               
using namespace std;
int fa[1000001], n, m, x, y;
int find(int x)//找到祖先,并将中间的点的父亲都修改成祖宗编号(路径压缩) 
{
    if(x!=fa[x])//当x不等于它的爸爸时(还有祖先) 
    	fa[x]=find(fa[x]);//找到祖先,并修改父亲
    return fa[x];//返回祖先 
}
void unity(int x, int y)
{
    int f1=find(x);//找到x的祖先 f1
    int f2=find(y);//找到y的祖先 f2
    fa[f1]=f2;//祖先和祖先结为父子(谁是父亲谁是儿子都可以) 
}
int main()
{
	while(true)
	{
		int ans=0;
		cin>>n>>m;
		if(n==0) return 0;
	    for(int i=1; i<=n; i++){
	    	fa[i]=i;//初始化自己的父亲是自己 
		}
	    for(int i=1; i<=m; i++){
	    	scanf("%d %d", &x, &y);//一点点把图连通起来
	        unity(x,y);//连通:合并x和y各自能到的地方
		}
	    for(int i=1; i<=n; i++){//自己的父亲等于自己本身
	    	if(find(i)==i) ans++;
		}
		printf("%d\n", ans-1);//共需修ans-1条路即可
	}
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

15个顶级元宇宙游戏

元宇宙游戏是可让数百万玩家在一个虚拟世界中相互互动&#xff0c;允许你按照自己的节奏玩游戏&#xff0c;并根据自己的条件推广自己的品牌。 而且&#xff0c;这些游戏中的大多数都涉及虚拟 NFT&#xff0c;它们是完全独特的和虚拟的。在 Facebook 将品牌重新命名为“Meta”…

Spring 国际化:i18n 如何使用

1、i18n概述 国际化也称作i18n&#xff0c;其来源是英文单词 internationalization的首末字符i和n&#xff0c;18为中间的字符数。由于软件发行可能面向多个国家&#xff0c;对于不同国家的用户&#xff0c;软件显示不同语言的过程就是国际化。通常来讲&#xff0c;软件中的国…

11月第2周榜单丨飞瓜数据B站UP主排行榜榜单(B站平台)发布!

飞瓜轻数发布2023年11月6日-11月12日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数、带货数据等维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营…

【JUC】六、辅助类

文章目录 1、CountDownLatch减少计数2、CyclicBarrier循环栅栏3、Semaphore信号灯 本篇整理JUC的几个同步辅助类&#xff1a; 减少计数&#xff1a;CountDownLatch循环栅栏&#xff1a;CyclicBarrier信号灯&#xff1a;Semaphore 1、CountDownLatch减少计数 案例&#xff1a;6…

基于opencv+tensorflow+神经网络的智能银行卡卡号识别系统——深度学习算法应用(含python、模型源码)+数据集(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 训练集图片处理1&#xff09;数据加载2&#xff09;图像处理 2. 测试图片处理1&#xff09;图像读取2&#xff09;图像处理 相关其它博客工程源代码下载其它资料下载 前言 本项目基于从网络获取的多种银行卡数据…

政府指导89元保330万 “聊惠保”2024年度正式上线!

11月15日&#xff0c;“聊惠保”2024年度启动仪式在聊城市融媒体中心举行。市政府领导&#xff0c;省直、市直相关部门单位和共保体成员单位负责同志参加仪式。“聊惠保”2024年度正式上线&#xff01;“聊惠保”项目组为聊城市医疗救助困难群体捐赠“聊惠保”2024年度团体保险…

python基础练习题库实验八

文章目录 前言题目1代码 题目2代码 题目3代码 总结 前言 &#x1f388;关于python小题库的这模块我已经两年半左右没有更新了&#xff0c;主要是在实习跟考研&#xff0c;目前已经上岸武汉某211计算机&#xff0c;目前重新学习这门课程&#xff0c;也做了一些新的题目 &#x…

LeetCode34-34. 在排序数组中查找元素的第一个和最后一个位置

&#x1f517;:代码随想录:二分查找的算法讲解:有关left<right和left<right的区别 class Solution {public int[] searchRange(int[] nums, int target) {int nnums.length;int l0,hn-1;if(numsnull){return null; }if(n0){return new int[]{-1,-1}; }if(target&l…

阿里云99元ECS云服务器老用户也能买,续费同价!

阿里云近日宣布了2023年的服务器优惠活动&#xff0c;令用户们振奋不已。最引人瞩目的消息是&#xff0c;阿里云放开了老用户的购买资格&#xff0c;99元服务器也可以供老用户购买&#xff0c;并且享受续费的99元优惠。此外&#xff0c;阿里云还推出了ECS经济型e实例&#xff0…

8年经验的软件工程师建议

我希望在职业生涯早期就开始做的事情和我希望以不同的方式做的事情。 大家好&#xff0c;我已经做了八年半的软件工程师。这篇文章来源于我最近对自己在职业生涯中希望早点开始做的事情以及希望以不同方式做的事情的自我反思。 我在这里分享的对任何希望提高和进步到高级甚至…

Java远程操作Linux服务器命令

Java可以通过SSH协议远程连接Linux服务器&#xff0c;然后使用JSch库或者Apache Commons Net库来执行远程Linux命令。以下是一个使用JSch库的示例代码&#xff1a; import com.jcraft.jsch.*;public class RemoteCommandExecutor {private String host;private String user;pr…

2023年亚太杯数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

ubuntu 20通过docker安装onlyoffice,并配置https访问

目录 一、安装docker &#xff08;一&#xff09;更新包列表和安装依赖项 &#xff08;二&#xff09;添加Docker的官方GPG密钥 &#xff08;三&#xff09;添加Docker存储库 &#xff08;四&#xff09;安装Docker &#xff08;五&#xff09;启动Docker服务并设置它随系…

2024年上半年:加密领域迎来无限机遇与重大突破!

2024年上半年将成为加密行业发展的关键时期&#xff0c;一系列重大事件和计划将为这一领域带来深远的影响。这些举措不仅有望吸引更多机构投资者和资金流入加密市场&#xff0c;还将进一步提升比特币的认可度和流动性&#xff0c;推动整个行业迈向新的阶段。 SEC批准比特币现货…

消息通讯——MQTT WebHookSpringBoot案例

目录 EMQX WebHook介绍EMQX WebHook是什么EMQX WebHook配置项如何使用EMQX WebHook配置WebHook配置事件推送参数详解 SpringBoot集成Webhook实现客户端断连监控1. 实现前提2. 代码实现接口3. 监听结果 总结 EMQX WebHook介绍 EMQX WebHook是什么 EMQX WebHook 是由 emqx_web_…

大语言模型概述|亚马逊这些互联网公司为什么花巨资训练自己的模型?

2023年可谓是大语言模型元年&#xff0c;OpenAI、亚马逊、谷歌等互联网公司争先恐后推出了自己的大语言模型&#xff1a;GPT-4、Titan、PaLM 2&#xff0c;还有亚马逊即将推出的第二个大语言模型Olympus等等。这一革命性技术如今已经在全球范围内引发了广泛的讨论和关注&#x…

保姆级fastDFS安装教程

一、软件准备 环境需要准备四个包&#xff0c;分别是&#xff1a; 1. libfastcommon_1.0.36 2. FastdfsFastdfs_v5.11 3. fastdfs-nginx-module5.11 4. nginxnginx-1.12.2 二、环境准备 安装perl环境&#xff0c;后续编译fastdfs会用到 yum -y install perl* yum -y ins…

Python武器库开发-flask篇之URL重定向(二十三)

flask篇之URL重定向(二十三) 通过url_for()函数构造动态的URL&#xff1a; 我们在flask之中不仅仅是可以匹配静态的URL&#xff0c;还可以通过url_for()这个函数构造动态的URL from flask import Flask from flask import url_forapp Flask(__name__)app.route(/) def inde…

为忙碌的软件工程师精心准备的编码面试准备材料,超过 100,000 人受益!

这是一个针对技术面试准备的手册。它收集了大量的面试问题和答案&#xff0c;涵盖了算法、系统设计、前端等主题&#xff0c;并且还在不断更新和完善中。 这个项目是“Tech Interview Handbook”&#xff0c;解决了求职者在技术面试中遇到的各种难题&#xff0c;帮助他们更好地…

Jenkins的一些其他操作

Jenkins的一些其他操作 1、代码仓库Gogs的搭建与配置 Gogs 是一款极易搭建的自助 Git 服务&#xff0c;它的目标在于打造一个最简单、快速和轻松的方式搭建 Git 服务。使用 Go 语言开发的它能够通过独立的二进制进行分发&#xff0c;支持了 Go 语言支持的所有平台&#xff0…