首页 > 其他分享 >最短路康复训练

最短路康复训练

时间:2023-04-21 20:58:51浏览次数:49  
标签:int 短路 vis push 康复训练 now dis

最短路

金字塔

比平常的最短路多加了个参数。
这里的路径长度计算与其他题不同,它是一条路径的长度+这条路径中最长的那条路的长度
用平常的最短路是行不通的,不能设置平常的vis数组,即(点出队列就不再更新)这个思路是错的,因为加上路径上的路的max可能就不一样了。

代码

void dijkstra(int s) 
{
	for(int i=1;i<=get(1,n);i++) dis[i]=inf,mx[i]=0;
	dis[s]=0;
	priority_queue<pii> q;
	q.push({0,s});
	while(q.size()) 
	{
		int u=q.top().second;
		q.pop();
		//if(vis[u]) continue;
		//vis[u]=1;
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(dis[j]+mx[j]>dis[u]+w[i]+max(mx[u],w[i])) 
			{
				dis[j]=dis[u]+w[i];
				mx[j]=max(mx[u],w[i]);
				if(!vis[j]) q.push({-dis[j],j});
			}
		}
	}
}

最短路树

思路

先dijkstra求一下点1到各个点的距离,然后扫一遍图,把dijkstra的最短路树弄出来,求出\(cnt[i]\),有多少个点经过i到点1,如果连接(1,i),则节省的长度为\(cnt[i]*(t-dis[i])\)。
该题还有个条件,字典序,从1到n枚举点,这样就保证了字典序

代码

const int N=2e5+10,inf=1e18;
int n,m,k;
int h[N],e[N*2],ne[N*2],w[N*2],idx;
int dis[N],cnt[N],pre[N];
bool vis[N];
vector<int> g[N];//存最短路树
void add(int a,int b,int c) 
{
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx++;
} 

void dijkstra(int s) 
{
	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
	dis[s]=0;
	priority_queue<pii> q;
	q.push({0,s});
	while(q.size()) 
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(dis[j]>dis[u]+w[i]) 
			{
				dis[j]=dis[u]+w[i];
				if(!vis[j]) q.push({-dis[j],j});
			}
		}
	}
}

void dfs(int u,int fa) 
{
	for(int i=0;i<g[u].size();i++) 
	{
		int j=g[u][i];
		if(j==fa) continue;
		dfs(j,u);
		cnt[u]+=cnt[j]; 
	}
}

void solve() 
{	
	memset(h,-1,sizeof(h));
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) cin>>cnt[i];
	for(int i=1;i<=m;i++) 
	{
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
		add(b,a,c);
	}
	dijkstra(1);
	int ans=0;
	for(int u=1;u<=n;u++) 
	{
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			//pre数组是如果dis[j]可以由多个点转移过来,则选最小的
			if(dis[j]==dis[u]+w[i]&&!pre[j]) 
			{
				g[u].push_back(j);
				pre[j]=u;
			}
		}
	}
	dfs(1,-1);
	for(int i=1;i<=n;i++) 
	{
		ans=max(ans,cnt[i]*(dis[i]-k));
	}
	cout<<ans<<endl;
}

通往奥格瑞玛的道路

题意

给出一个图,图有两个信息:每个城市会收钱,每条路会扣血,主角血条为K,问从1走到n,血不扣到负数的情况下,经过的城市收费最大值的最小值。

思路

二分+最短路。一开始subtask1中第三个点老是过不去,最后看题解,???我一开始就在城市1它都要收费啊?

代码

bool dijkstra(int s,int x) 
{	
	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
	priority_queue<pii> q;
	dis[s]=0;
	q.push({0,s});
	while(q.size()) 
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(f[j]>x) continue;
			if(dis[j]>dis[u]+w[i]) 
			{
				dis[j]=dis[u]+w[i];
				if(!vis[j]) q.push({-dis[j],j});
			}
		}
	}
	return dis[n]<=k;
}

void solve() 
{		
	memset(h,-1,sizeof(h));
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) cin>>f[i];
	for(int i=1;i<=m;i++) 
	{
		int a,b,c;
		cin>>a>>b>>c;
		if(a==b) continue;
		add(a,b,c);
		add(b,a,c);
	}
	int l=f[1],r=inf;//l要从城市1收的钱开始
	while(l<r) 
	{
		int mid=(l+r)/2;
		if(dijkstra(1,mid)) r=mid;
		else l=mid+1;
	}
	if(r==inf) cout<<"AFK\n";
	else cout<<r<<endl;
}

P5837

题意

图中两个信息:每条管道的花费、流量,求1到n的流量与花费之比的最大值。

思路

首先花费能小就小,这就是求最短路了。
在上面的基础上增加约束条件:流量,从1到n的总流量取的是这条路中的管道的最小值,
题目说花费和流量都是1~1000的正整数,直接枚举最小流量做1000遍最短路。
好在它数据范围比较小,大点就用二分。
同类型题:P3063
上面的那题也差不多

代码

double dijkstra(int s,int x) 
{	
	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
	priority_queue<pii> q;
	dis[s]=0;
	q.push({0,s});
	while(q.size()) 
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(f[i]<x) continue;//x已经是最小了,别比它更小
			if(dis[j]>dis[u]+w[i]) 
			{	
				dis[j]=dis[u]+w[i];
				if(!vis[j]) q.push({-dis[j],j});
			}
		}
	}
	if(dis[n]==inf) return -1;
	return x*1.0/dis[n];
}

void solve() 
{		
	memset(h,-1,sizeof(h));
	cin>>n>>m;
	for(int i=1;i<=m;i++) 
	{
		int a,b,c,d;
		cin>>a>>b>>c>>d;
		if(a==b) continue;
		add(a,b,c,d);
		add(b,a,c,d);
	}
	int ans=0;
	for(int i=1;i<=1000;i++) 
	{	
		int k=dijkstra(1,i)*1e6;
		ans=max(ans,k);
	}
	cout<<ans<<endl;
}

P2047

思路

范围只有100,处理各个点对间的问题。 floyd的拿手好戏。
求最短路的时候顺便把两个点间最短路的条数也处理一下。
之后就是根据题目要求算答案了

代码

void floyd() 
{
	for(int k=1;k<=n;k++) 
	{
		for(int i=1;i<=n;i++) 
		{
			for(int j=1;j<=n;j++) 
			{
				if(dis[i][j]>dis[i][k]+dis[k][j]) 
				{
					dis[i][j]=dis[i][k]+dis[k][j];
					e[i][j]=e[i][k]*e[k][j];
				}
				else if(dis[i][j]==dis[i][k]+dis[k][j])
				{
					e[i][j]+=e[i][k]*e[k][j]; 
				}
			}
		}
	}
	for(int k=1;k<=n;k++) 
	{
		for(int i=1;i<=n;i++) 
		{
			for(int j=1;j<=n;j++) 
			{
				if(i==j||j==k||i==k) continue;
				if(e[i][j]==0) continue;
				if(dis[i][j]==dis[i][k]+dis[k][j]) 
				{
					ans[k]+=1.0*(e[i][k]*e[k][j])/e[i][j];
				}
			}
		}
	}
}

void solve() 
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) 
	{
		for(int j=i+1;j<=n;j++) 
		{
			dis[i][j]=dis[j][i]=inf;
		}
	}
	for(int i=1;i<=m;i++) 
	{
		int a,b,c;
		cin>>a>>b>>c;
		dis[a][b]=dis[b][a]=min(dis[a][b],c);
		e[a][b]=e[b][a]=1;
	}
	floyd();
	for(int i=1;i<=n;i++) 
	{
		cout<<fixed<<setprecision(3)<<ans[i]<<endl;
	}
}

P2176

题意

原本有个图,问将某一条边加倍后,最短路与之前的最短路的最大差值

思路

先将原来的图求一遍最短路,然后把最短路内的所有边都记录,加倍的路就是这些路的其中一条,因为最短路外的边加倍那图的最短路也没变。
那如果原来的最短路不止一条上面的做法会不会失效?不会,假设有两条最短路,两种情况:
如果它们没有公共的边,那么选任何一条最短路的边来加倍,那之后的图最短路就是之前没改的那一条
如果它们有公共的边,那么最优解肯定选它们的公共边的某一条加倍(不一定要选公共边中最长的那条,想想为什么)

时间复杂度:n为100,原图最短路含边最多为n-1,做n-1遍最短路,
复杂度为\(O((n-1)(n+m)logm)\), \(100*(100+5000)*log_2{5000} \approx 1300*5100\)

代码

const int N=210,inf=1e18;
int n,m,k,s,t;
int e[N][N],pre[N],dis[N];
vector<int> g[N];
bool vis[N];
void dijkstra(int s,bool flag) 
{
	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
	dis[s]=0;
	priority_queue<pii> q;
	q.push({0,s});
	while(q.size()) 
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=0;i<g[u].size();i++) 
		{
			int j=g[u][i];
			if(dis[j]>dis[u]+e[u][j]) 
			{
				dis[j]=dis[u]+e[u][j];
				if(flag) pre[j]=u;
				if(!vis[j]) q.push({-dis[j],j});
			}
		}
	}
}

void solve() 
{	
	cin>>n>>m;
	for(int i=1;i<=m;i++) 
	{
		int a,b,c;
		cin>>a>>b>>c;
		e[a][b]=e[b][a]=c;
		g[a].push_back(b);
		g[b].push_back(a);
	}
	dijkstra(1,1);
	int k=dis[n],mx=0;
	int now=n;
	while(now!=1) 
	{	
		e[now][pre[now]]*=2;
		e[pre[now]][now]*=2;
		dijkstra(1,0);
		mx=max(mx,dis[n]-k);
		e[now][pre[now]]/=2;
		e[pre[now]][now]/=2;
		now=pre[now];
	}
	cout<<mx<<endl;
}

2023.4.21

这一个星期刷了下最短路的题,把有意思的题记录了一下。上个大学怎么这么多事啊,java课设、计组实验、数模论文、明天就是天梯赛了,这个星期可能是这个学期最累的一个星期了。

标签:int,短路,vis,push,康复训练,now,dis
From: https://www.cnblogs.com/LIang2003/p/17341751.html

相关文章

  • 图的最短路问题(综合详解!!!看这一篇就够了)(spfa-Dijkstra-floyd-模板代码c-)
    文章目录图论:三种最短路及模板模板SPFA算法Floyd算法Dijkstra算法例题与应用反向建边最短路计数1488.最短距离3305.作物杂交4074.铁路与公路图论:三种最短路及模板注意:在这三种算法中我均使用的链式前向星存图,具体方式请看我这篇博客:链式前向星存图详解模板SPFA算法spfa是优化......
  • POJ 1502 MPI Maelstrom(最短路)
    MPIMaelstromTimeLimit: 1000MS MemoryLimit: 10000KTotalSubmissions: 5476 Accepted: 3409DescriptionBIThasrecentlytakendeliveryoftheirnewsupercomputer,a32processorApolloOdysseydistributedsharedmemorymachinewithahierarchica......
  • 无题 (最短路)
    无题DescriptionTanvirreturnedhomefromthecontestandgotangryafterseeinghisroomdusty.Wholikestoseeadustyroomafterabrainstormingprogrammingcontest?Aftercheckingabithefoundthatthereisnobrushinhimroom.So,hecalledAtiq......
  • 迷宫最短路径
    定义一个二维数组: intmaze[n][m]; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。intmaze[6][7]={0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,1,0,1,......
  • 讲课:拓扑排序、最短路算法
    什么是图?把图在计算机中表示(储存)拓扑排序度与一个顶点v关联的边的条数称作该顶点的度(degree)在有向图G=(V,E)中,以一个顶点v为起点的边的条数称为该顶点的出度(out-degree),以一个顶点v为终点的边的条数称为该节点的入度(in-degree)思路首先记录各......
  • free (牛客多校) (dj最短路+dp优化处理)
    本题大意:给出n,m,s,t,k,n个点,m条路,求s到t的最短路,并且最多k条路免费,然后给出m行,u,v,w,代表u到v有一条权值为w的双向路。 思路:就是dj最短路+一个dp维度的处理,dp[i][j],到第i个节点用了多少个免费的路径的最短路径 ......
  • 最短路合辑
    Dijkstra算法,堆优化版本复杂度是mlog(m)。适合于没有负权的图。#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;constintN=1e5+5;constintINF=0x3f3f3f3f;vector<pair<int,int>>G[N];intvis[N];intdist[N];voiddij(ints){......
  • 2642. 设计可以求最短路径的图类
    题目链接:2642.设计可以求最短路径的图类方法一:Dijkstra解题思路每次调用\(shortestPath(st,ed)\)时,就通过\(Dijkstra\)算法计算\(st\)->\(ed\)的最短路。代码朴素写法classGraph{private:vector<vector<int>>adj;inte[110][110],n;public:G......
  • Dijkstra算法求最短路
    一、Dijkstra 只适用于单源最短路中所有边权都是正数的情况二、存储方式1、稠密图用邻接矩阵2、稀疏图用邻接表三、算法实现用一个dist数组保存源点到其余各个节点的距离,dist[i]表示源点到节点i的距离。将dist数组赋值为正无穷,dist[1]=0用......
  • UVA 12295 Optimal Symmetric Paths 最短路求方案数
    题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23587题意:给一个n*n的矩阵,每个方格中有一个数字,从左上角走到右下角,且路径必须关于副对角线对称,求使路线上数字和最小的方案数思路:既然要关于副对角线对称,那么可以把关于副对角线对称的方格的值加到一起去,这样就......