【算法每日一练]-图论(保姆级教程篇14 )#会议(模板题) #医院设置 #虫洞 #无序字母对 #旅行计划 #最优贸易

news2024/12/26 11:47:36

目录

今日知识点:

求数的重心先dfs出d[1]和cnt[i],然后从1进行dp求解所有d[i]

两两点配对的建图方式,检查是否有环

无向图欧拉路径+路径输出

topo+dp求以i为终点的游览城市数

建立分层图转化盈利问题成求最长路

会议(模板题)

医院设置 

虫洞

无序字母对 

旅行计划

最优贸易


        

        

        

会议(模板题)

         

 思路:

补充:首先,阅读题目可以看出来,这道题目实际上就是求树的重心。

树的重心找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,达到的效果是生成的多棵树尽可能平衡。

啊?不是n-1边n点的无向图吗?怎么就一定是棵树了呢?万一是“X”型或者“米”型的无向图呢,哈哈我可太聪明了。

你直接从任意一个点做根开始,然后把该点其余的分支点都掰到下面去。也就是说把其余的子树都扭动到下面去,这不就是一颗树了吗。明白了吗!

OK,怎么解这道题呢?

举个例子:

我们不妨设置d[i]表示以此点为根的所有点总距离和,cnt[i]表示以此为根的节点数

我们首先知道d[1]=16,cnt[1]=10我们来看d[2]应该怎么求,我们发现相对于d[1]来说,如果设2为最佳点,2,5,6其距离-1,剩下的1,4,3,7,8,9,10到其距离+1。

故:d[2]=d[1] - 3 + 7 =20

其中3是子根2对应的节点数cnt[2],7是1为子根对应的节点数cnt[1]-cnt[2]

得:d[i]=d[fa]-cnt[i]+(cnt[1]-cnt[i])

那么只需要先dfs求出来d[1]和每个点的cnt[i]。然后就可以进行dp最终求出所有点的d[i]。

 

#include <bits/stdc++.h>
using namespace std;
const int N=50005;
int minn=0x3f3f3f3f,ans,n,d[N],cnt[N];
vector<int>ve[N];
void dfs(int u,int fa,int len){//一定别走fa回去
	cnt[u]++;//先加上自己
	for(int i=0;i<ve[u].size();i++){
		int v=ve[u][i];
		if(v==fa)continue;
		dfs(v,u,len+1);//先求孩子的cnt,之后求自己cnt
		cnt[u]+=cnt[v];
	}
	d[1]+=len;//最后求d[1]
}
void dp(int u,int fa){
	for(int i=0;i<ve[u].size();i++){
		int v=ve[u][i];
		if(v==fa)continue;
		d[v]=d[u]-2*cnt[v]+cnt[1];
		dp(v,u);//这里对自己进行转移更新,再对孩子的更新
	}
}
int main(){
	cin>>n;int a,b;
	for(int i=1;i<n;i++){
		cin>>a>>b;
		ve[a].push_back(b);
		ve[b].push_back(a);
	}
	dfs(1,0,0);
	dp(1,0);
	for(int i=1;i<=n;i++){
		if(d[i]<minn)
			minn=d[i],ans=i;
	}
	cout<<ans<<" "<<minn;
}

上面我打注释的地方一定要理解 

        

        

医院设置 

 

 

思路:

还是一道求树的重心题。不过是每个点都有一个权值。那么把权值当成“另一个世界的节点数”就好了。然后不断求cnt,之后dp就行。 

#include <bits/stdc++.h>
using namespace std;
const int N=500;
int ans=0x3f3f3f3f,n,d[N],cnt[N],w[N];
vector<int>ve[N];
void dfs(int u,int fa,int len){
	cnt[u]=w[u];//这里还是先加自己
	for(int i=0;i<ve[u].size();i++){
		int v=ve[u][i];
		if(v==fa)continue;
		dfs(v,u,len+1);
		cnt[u]+=cnt[v];
	}
	d[1]+=len*w[u];//更新d[1]也要变一下
}
void dp(int u,int fa){
	for(int i=0;i<ve[u].size();i++){
		int v=ve[u][i];
		if(v==fa)continue;
		d[v]=d[u]+cnt[1]-cnt[v]*2;
		dp(v,u);
	}
	ans=min(ans,d[u]);
}
int main(){
	cin>>n;int c,a,b;
	for(int i=1;i<=n;i++){
		cin>>c>>a>>b;
		w[i]=c;//注意输入方式
		if(a)ve[i].push_back(a),ve[a].push_back(i);
		if(b)ve[i].push_back(b),ve[b].push_back(i);
	}
	dfs(1,0,0);
	dp(1,0);
	cout<<ans;
}

        

        

虫洞

        

思路:
首先分析一下:第一是如何去建图,其次是如何找方案

找方案的话就直接暴力配对吧(题上的数据量不大,肯定要暴力),然后就是建图要保证即要方便图的还原(因为配对后还要回溯呢),又要方便跑图(每次建好都要跑一次看看是否存在一个循环),最后就是坐标范围非常大啊,所以要巧妙一点。

首先:我们对这些黑洞位置排序,只关注同行的,同行中能从A黑洞走到B黑洞的就标记一下A能到B(使用唯一的ID号映射)。

之后:dfs选择配对方案,然后dfs的for函数也是很巧妙:首先要保证不能和前面重复(相当于1号黑洞可以找2,3,4,但是之后4号一定不能再找1,2,3了,所以要保证递增)然后是g[i]=k;g[k]=i这样建图(因为是两两配对,所以每个起点最多只有一个尾点)。最后是建好图后直接从每个黑洞ID号为起点进行查询即可。

最后是检查函数cycle:从起点一直走,走到走过的就可以停止了

#include <bits/stdc++.h>
using namespace std;
int ans,n,to[20],vis[20],g[20];
struct node{int x,y;}p[20];

bool cmp(node a,node b){
	return (a.y<b.y)||(a.y==b.y&&a.x<b.x);
}

int cycle(int x){
	while(to[x]){//走到下一个黑洞,如果有的话
		if(vis[x])return 1;
		vis[x]=1;
		x=g[to[x]];//终点变成起点(如果有的话)
	}
	return 0;
}
void dfs(int k){
	if(k>n){
		int f=0;
		for(int i=1;i<=n;i++){//巧妙3:直接从黑洞号开始就行
			memset(vis,0,sizeof(vis));
			f|=cycle(i);//巧妙4:这里之所以这么写,是为了防止被标记过1后又被0覆盖掉
		}
		ans+=f;
		return ;
	}
	if(g[k])dfs(k+1);
	else {
		for(int i=k+1;i<=n;i++){//巧妙1:去重
			if(g[i])continue;
			g[i]=k;g[k]=i;//巧妙2:设置两个黑洞的关系
			dfs(k+1);
			g[i]=g[k]=0;//清除两个黑洞之间的关系
		}
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>p[i].x>>p[i].y;
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n-1;i++){
		if(p[i].y==p[i+1].y) to[i]=i+1;//把相邻且在同一航 行的标记在一起
	}
	dfs(1);
	cout<<ans;
	
}

首先是方案配对,然后是建图策略(至多两两配对),最后到检查循环都是非常精妙的 ,值得细看

         

        

无序字母对 

思路: 

这里就可以发现实际上就是在找欧拉路,首先每个字符就是代表的图中的某一个点,底下输入的字符串
就代表两点之间有连通,构造字符串就是在找输出一笔画回路,明白这个代码就很简单了

 下面是代码:基于无向图得欧拉路径问题+路径输出

无向图得欧拉路径:连通图,且没有度为奇数的节点(欧拉回路) 或 只有两个2个度为奇数的节点 

#include <bits/stdc++.h>
using namespace std;
const int N=300;
int g[N][N],fa[N],du[N],m;
char ans[N*N],s[N];
int find(int x)
{
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];//返回祖先 
}

void dfs(int x){
	for(int i=0;i<N;i++)
		if(g[x][i]){
		g[x][i]=g[i][x]=0;//取过了这个字母就情空,避免走环 g=0相当于vis=1
		dfs(i);
	}
	ans[m--]=x;
}

int main()
{
	cin>>m;
    for(int i=0; i<N; i++) fa[i]=i;
    for(int i=1; i<=m; i++){
    	scanf("%s",s);
    	g[s[0]][s[1]]=g[s[1]][s[0]]=1;
        du[s[0]]++;du[s[1]]++;
        int f1=find(s[0]),f2=find(s[1]);//合并建树
        fa[f1]=f2;
	}
//判断是否连通
	int tmp=0;
	for(int i=0;i<N;i++)
		if(fa[i]==i&&du[i])tmp++;
	if(tmp!=1){
		cout<<"No Solution";return 0;
	}
//是否存在欧拉路径	
	int f=0,rt=0;
	for(int i=0;i<N;i++){
		if(du[i]&1){
			f++;	//一边统计多少个奇数度点,一边找奇数的rt做起点
			if(!rt)rt=i;
		}
	}
	if(f&&f!=2){
		cout<<"No Solution";return 0;
	}
//按照字典序最小开始输出路径
	if(!rt){//rt不能从0开始
		for(int i=0;i<N;i++){//按照ASCII找最小的起点rt
			if(du[i]){
				rt=i;break;
			}
		}
	}
	dfs(rt);
	cout<<ans;

}

        

         

旅行计划

 

思路:

题上问以i点为终点的最多游览的城市数。非常类似之前说过的“食物链计数”那道题。

设置f[i]表示以i为终点的最多的游览城市数。那么从入度为0的点开始进行正向topo即可。

顺便再补充一个方向:如果设置f[i]表示以i为起点的最多的游览城市数。那么肯定不能正向topo。

这个时候需要用dfs进行回溯式topo处理。

#include<bits/stdc++.h> 
using namespace std;
const int N=100005;
vector <int>ve[N];
queue<int> q;
int n,m,ans,f[N],in[N],out[N];

int main(){
	cin>>n>>m;int x,y;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		ve[x].push_back(y);
		in[y]++;out[x]++;
	}
	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=ve[cur].size();i<sz;i++){
			int v=ve[cur][i];
			f[v]=f[cur]+1;in[v]--;
			if(in[v]==0) q.push(v);
		}		
	}

	for(int i=1;i<=n;i++)
	cout<<f[i]<<'\n';
	
	
	return 0;
}

        

         

最优贸易

 

思路:

 每个点都可以不卖不买,买,或卖这3种状态,那么分层图自然最合适。

最上面的层之间不论怎么跑动一定不会赚钱或亏钱。只有在层之间移动才能赚钱或亏钱。

也就是层内关系权值为0,层间非0.

然后就转化成spfa求最长路即可。 

#include <bits/stdc++.h> 
using namespace std;
const int N=1e5+5;
int cnt,head[N*3];
int dis[N*3];
bool vis[N*3];
struct Edge{ int to,w,next;}e[250000*3];
void add(int u,int v,int w) { e[++cnt]=(Edge){v,w,head[u]}; head[u]=cnt;}
void spfa(int s)
{
    memset(dis,-0x3f,sizeof(dis));
    queue<int>Q;
    dis[s]=0;vis[s]=1;
    Q.push(s);
    while(!Q.empty())
    {
		int u=Q.front(); Q.pop();
		vis[u]=0;
	    for(int i=head[u];i;i=e[i].next)
	    {
	        int v=e[i].to,w=e[i].w;
	        if(dis[v]<dis[u]+w) {
	        	dis[v]=dis[u]+w;
				if(vis[v])continue;
				Q.push(v);vis[v]=1;	
			}
	    }
    }    
}

int main()
{
	int n,m,price;
	cin>>n>>m; 
	for(int i=1;i<=n;i++){
		cin>>price;
		add(i,i+n,-price);//在层之间建立关系
		add(i+n,i+n*2,price);
	}
    int u,v,c;
    for(int i=0;i<m;++i)
    {
    	cin>>u>>v>>c;
    	add(u,v,0);add(u+n,v+n,0);add(u+2*n,v+2*n,0);//建立 分层图(层内图)
    	if(c==2){
    	add(v,u,0);add(v+n,u+n,0);add(v+2*n,u+2*n,0);
		}
    }
    spfa(1);
    printf("%d",dis[n+2*n]);
    return 0;
}

然后做完这道题,可以很明显发现和之前做过的“飞行路线”一题很像。那道题中是层内关系的权值非0,层间的关系权值为0,最后在最下面的层找答案即可。本题刚好反过来。

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

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

相关文章

I.MX6ULL开发笔记(二)——硬件外设操作

0x01 点亮第一个RGB灯 在文章http://t.csdnimg.cn/EGWt9中有介绍Linux下文件目录&#xff0c;那么在Linux系统下&#xff0c;RGB灯也是一个设备&#xff0c;所以我们需要到/sys目录下去操作这个设备。 之后&#xff0c;我们进入到class目录&#xff0c;这里挂载着开发板上的外…

【数据仓库与联机分析处理】多维数据模型

目录 一、数据立方体 二、数据模型 &#xff08;一&#xff09;星形模型 &#xff08;二&#xff09;雪花模式 &#xff08;三&#xff09;事实星座模式 三、多维数据模型中的OLAP操作 &#xff08;一&#xff09;下钻 &#xff08;二&#xff09;上卷 &#xff08;三…

【GitHub】-design-pattern-extend(设计模式扩展)

写在前面 偶然间看到一篇文章 《Java 中保持扩展性的几种套路和实现》&#xff0c;写的不错&#xff0c;但是类图画的差了点儿意思。于是&#xff0c;自己动手画了画&#xff0c;对其中的内容作了一些调整&#xff0c;对包做了进一步划分&#xff0c;便于理解消化。以下是对Git…

第11章 GUI Page462~476 步骤二十三,二十四,二十五 Undo/Redo ③实现“Undo/Redo”菜单项

工程六 添加“编辑”菜单和子菜单 菜单ID分别为 idMenuEditUndo 和 idMenuEditRedo 热键&#xff08;快捷键&#xff09;分别为CtrlZ 和 CtrlShiftZ 变量名分别为 MenuItemEditUndo 和 MenuItemEditRedo 分别添加事件 ActionLink类增加成员函数 运行效果&#xff1a;“添加…

【C语言】Linux实现高并发处理的过程

一、实现高并发的几种策略 C语言本身并没有内建的多线程支持&#xff08;新版C语言支持&#xff0c;但用得不多&#xff09;&#xff0c;但是在多数操作系统中&#xff0c;可以使用库来实现多线程编程。例如&#xff0c;在POSIX兼容系统上&#xff0c;可以使用 pthreads 库来创…

【Web开发】会话管理与无 Cookie 环境下的实现策略

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; Web开发 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 问题&#xff1a; 思路&#xff1a; 方法&#xff1a; 结语 我的其他博客 前言 在当今Web应用程序中&#xff0c;会话…

实时语义分割模型PP-LiteSeg论文解读

paper&#xff1a;PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model official implementation&#xff1a;https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.8/paddleseg/models/pp_liteseg.py 本文的创新点 提出了一种灵活的轻量级解码器&#xf…

力扣:438. 找到字符串中所有字母异位词 题解

Problem: 438. 找到字符串中所有字母异位词 438. 找到字符串中所有字母异位词 预备知识解题思路复杂度Code其它细节推荐博客或题目博客题目滑动窗口哈希表 预备知识 此题用到了双指针算法中的滑动窗口思想&#xff0c;以及哈希表的运用。c中是unordered_map。如果对此不了解的u…

基于Spring-boot-websocket的聊天应用开发总结

目录 1.概述 1.1 Websocket 1.2 STOMP 1.3 源码 2.Springboot集成WS 2.1 添加依赖 2.2 ws配置 2.2.1 WebSocketMessageBrokerConfigurer 2.2.2 ChatController 2.2.3 ChatInRoomController 2.2.4 ChatToUserController 2.3 前端聊天配置 2.3.1 index.html和main.j…

路由器01_工作原理

一、回顾交换机工作原理 交换机里面维护了一张MAC地址表&#xff0c;主要记录的是MAC地址和接口的对应关系。 交换机在初始状态下&#xff0c;MAC地址表是空的&#xff0c;当收到一个来自某接口的数据时&#xff0c;首先查看数据帧中的MAC地址表&#xff0c;对照自己的MAC地址…

MySQL进阶篇(二) 索引

一、索引概述 1. 介绍 索引&#xff08;index&#xff09;是帮助 MySQL 高效获取数据 的 数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数…

React 实现拖放功能

介绍 本篇文章将会使用react实现简单拖放功能。 样例 布局拖放 LayoutResize.js import React, {useState} from "react"; import { Button } from "antd"; import "./LayoutResize.css";export const LayoutResize () > {const [state,…

imgaug库指南(六):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

海康威视安全接入网关 任意文件读取漏洞复现

0x01 产品简介 海康威视安全接入网关是一种网络安全产品,旨在提供安全、可靠的远程访问和连接解决方案. 0x02 漏洞概述 海康威视安全接入网关使用Jquery-1.7.2 , 该版本存在任意文件读取漏洞,可获取服务器内部敏感信息泄露(安博通应用网关也存在此漏洞) 0x03 复现环境 …

未完成销量任务的智己汽车突发大规模车机故障,竞争压力不小

2024年刚开年&#xff0c;智己汽车便上演了一出“开门黑”。 近日&#xff0c;不少车主在社交平台发帖&#xff0c;反映智己LS6出现大规模车机故障&#xff0c;包括但不限于主驾驶屏幕不显示车速、档位、行驶里程&#xff0c;左右转盲区显示失效&#xff0c;无转向灯、雷达提醒…

04 帧 Frame

文章目录 04 帧 Frame4.1 相机相关信息4.2 特征点提取4.2.1 特征点提取 ExtractORB()4.3 ORB-SLAM2对双目/RGBD特征点的预处理4.3.1 双目视差公式4.3.2 双目图像特征点匹配 ComputeStereoMatches()4.3.3 根据深度信息构造虚拟右目图像&#xff1a;ComputeStereoFromRGBD() 4.4 …

Unity 欧盟UMP用户隐私协议Android接入指南

Unity 欧盟UMP用户协议Android接入指南 官方文档链接开始接入mainTemplate.gradle 中引入CustomUnityPlayerActivity 导入UMP相关的包java类中新增字段初始化UMPSDK方法调用![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d882171b068c46a1b956e80425f3a9cf.png)测…

【STM32】STM32学习笔记-ADC单通道 ADC多通道(22)

00. 目录 文章目录 00. 目录01. ADC简介02. ADC相关API2.1 RCC_ADCCLKConfig2.2 ADC_RegularChannelConfig2.3 ADC_Init2.4 ADC_InitTypeDef2.5 ADC_Cmd2.6 ADC_ResetCalibration2.7 ADC_GetResetCalibrationStatus2.8 ADC_StartCalibration2.9 ADC_GetCalibrationStatus2.10 A…

网络优化篇(一)---------TCP重传性能优化

本文通过一个TCP重传优化的实际问题,详细讲解问题的分析、定位、优化过程。 通过本文你将学到: 如何通过linux命令和/proc文件系统分析TCP性能数据如何通过linux命令和netlink api分析某个具体的TCP连接的性能数据如何通过bcc工具分析TCP性能数据如何通过调整系统参数优化TCP重…

63.接口安全设计(活动管理系统:三)

文章目录 一、参数校验二、统一封装返回值三、做权限控制四、加验证码五、 限流六、加ip白名单七、校验敏感词八、使用https协议九、数据加密十、做风险控制 在日常工作中&#xff0c;开发接口是必不可少的事情&#xff0c;无论是RPC接口还是HTTP接口&#xff0c;我们都应该考虑…