红色彼岸花,花瓣|

Vanyun

园龄:4年6个月粉丝:18关注:23

最短路

最短路

其实最短路来自图论 , 所以先不讲最短路 , 先来讲讲关于图的基本知识

存图

链式前向星

好简单直接过

但是!!!如果是无向边数组记得开双倍空间呀!

放个代码:

struct egde{
	int x, y, z, next;
}e[maxm<<1];//无向图开双倍空间

int head[maxn], vis[maxn], dis[maxn], cnt;

inline void add_edge( int x , int y , int z ){
	e[++cnt].x = x , e[cnt].y = y , e[cnt].z = z;
	e[cnt].next = head[x];
	head[x] = cnt;
}

for(int i = head[now]; i; i = e[i].next){
    /*do something*/
}

我个人比较喜欢用vector存图

邻接矩阵

这个更暴力了,所以不讲。这里讲一个队邻接矩阵的优化:

用vector实现邻接矩阵(其实这个时候已经叫领接表了)STL大法好

代码:

vector<pair<int, int> > v[maxn];

void add_edge(int x, int y, int z){
	return (void)(v[x].push_back(make_pair(y,z)), v[y].push_back(make_pair(x,z)));
} 

for(int i = 0; i < v[now].size(); i ++){
	int to = v[now][i].first, len = v[now][i].second;
	/*do someting*/
}

多好

图的遍历

基于栈的DFS

象征性放一下代

void dfs(int now){
    vis[now] = 1;
    for(int i = 0; i < v[now].size(); i ++){
        int to = v[now][i];
        if(!vis[to]) dfs(to);
    }
    return;
}

基于队列的BFS

void bfs(int x){
	queue<int> q;
    q.push(x), vis[x] = 1;
    while(q.size()){
	int now = q.front();
        q.pop();
        for(int i = 0; i < v[now].size(); i ++){
	    int to = v[now][i];
            if(!vis[to]) q.push(to), vis[now] = 1;
        }
    }
    return;
} 

最短路算法

SPFA

用于求解单源最短路问题,即求-一个点到图上其它点的最短路。

BellmanFord的优化。

算法描述:

  1. 对每个点x设置一个变量dis[x], 表示它离起点8的最短距离,令dis[x]=0
  2. 维护一个队列,先将起点入队。
  3. 每次从队列中取出一个点,用它去更新所有相邻的点。
  4. dis[to]=min(dis[to], dis[now]+len)
  5. 在一个点被更新后,如果它不在队列中,则将它入队。

时间复杂度上界仍然为O(nm),但对于随机数据,该算法效率非常高。

说人话:SPFA非常非常非常容易被卡qwq。

代码:

void SPFA(int x){
    queue<int> q;
    q.push(x), vis[x] = 1, dis[x] = 0;
    while(q.size()){
        int now = q.front();
        q.pop(), vis[now] = 0;
        for(int i = 0; i < v[now].size(); i ++){
            int to = v[now][i].first, len = v[now][i].second;
            if(dis[to] > dis[now] + len){
		dis[to] = dis[now] + len;
                if(!vis[to]){
		    q.push(to);
                    vis[to] = 1;
                }
            }
        }
    }
    return;
}

Dijkstra

用于求单源最短路问题

算法描述:

  1. 将点分为两类, 一类是最短路已确定的点,另一类是最短路未确定的点。
  2. 对每个点x设置一个变量dis[x],表示它离起点的最短距离。
  3. 首先将起点x标记为最短路已确定的点,即vis[x]=1,并且dis[x]=0
  4. 每次从所有最短路未确定的点中取出离起点最近的点,将它标记为最短路已经确定,用它去更新与它相邻的所有点。
  5. dis[to]=min(dist[to], dis[now]+len)
  6. 时间复杂度为O(n2)

堆优化:使用链式前向星或邻接矩阵存图,用堆来优化寻找最近点的过程,时间复杂度降至O(mlogn)

Dijkstra不会被卡,因为他的时间复杂度是严格的O(mlogn)

注意啦:因为Dijkstra的算法原理是贪心,而出现负边权的话我们显然可以发现贪心是错误的,所以Dijkstra无法解决带有负权值得最短路问题。

这里只提供O(mlogn)的写法。

代码:

void Dijkstra(){
	priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > q;
	q.push(make_pair(0,1));
	dis[1] = 0;
	while(q.size()){
		int now = q.top().second, len = q.top().first;
		q.pop();
		if(vis[now]) continue;
		vis[now] = 1;
		for(int i = 0; i < v[now].size(); i ++){
			int to = v[now][i].first, weight = v[now][i].second;
			if(dis[to] > len + weight){
				dis[to] = len + weight;
				q.push_back(make_pair(dis[to],to));
			}
		}
	}
	return;
}

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(i != j && j != k && k != i && dis[i][j] > dis[i][k] + dis[k][j])
				      dis[i][j] = dis[i][k] + dis[k][j];
	return;
}

最短路径输出

没用的小知识qwq。

在更新最短路的时候用pre数组记录前驱即可。

代码:

void print(int n){
    if(!pre[n]) return;
    print(pre[n]);
    printf("%d", n);
}

最短路计数

模板

这个点的路径数可以由上一个点转移过来。

代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10, mod = 100003;

vector<int> v[maxn];
int n, m, dis[maxn], vis[maxn], sum[maxn];

void BFS(int x){
	queue<int> q;
	q.push(x), vis[x] = 1, dis[x] = 0;
	while(q.size()){
		int now = q.front();
		q.pop();
		for(int i = 0; i < v[now].size(); i ++){
			int to = v[now][i];
			if(dis[to] > dis[now] + 1){
				dis[to] = dis[now] + 1;
				sum[to] = sum[now];
				vis[to] = 1;
				q.push(to);
			}else if(vis[to] and dis[to] == dis[now] + 1){
				sum[to] += sum[now];
				sum[to] %= mod;
			}
		}
	}
}

signed main(){
	memset(dis, 0x3f, sizeof(dis));
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++){
		sum[i] = 1;
	}
	for(int i = 1, x, y; i <= m; i ++){
		scanf("%d%d", &x, &y);
		v[x].push_back(y);
		v[y].push_back(x);
	}
	BFS(1);
	for(int i = 1; i <= n; i ++){
		if(vis[i]) printf("%d\n", sum[i]);
		else printf("0\n");
	}
	return 0;
}

分层最短路

分成最短路其实就是把一个图分层然后我们来跑一遍最短路。通俗易懂的语言==废话​

分层最短路的关键在于我们如何去给这个图分层。

为例。 是道裸题

我们会发现这道题很熟悉,所以仔细观察,我们还会发现k非常的小。

我们考虑如何给图分层,首先我们把原来的图原封不动的复制k遍(具体实现过程并不是这样的,这样讲方便理解)。

每一层之间我们以权值为0的边连接.....我也口胡不清楚,上图。

以样例为例(图来自luogu)

首先我们把n个点变为了n+n×k个点,其中每ii+n1为一层好吧。

层与层之间的连接:如果x这个点到y这个点在原图上有边,那我们便在x+n×(i1)y+n×i上连一条边权为0的边表示使用免费次数,以此类推....

这样下来我们的图便分好层了。

分层的意义在于我从x点到y点的最短路径上可以保证使用的免费飞行次数k次,但肯定是使用的次数越多优的qwq。

有个小细节需要注意,我们分层最短路相当于把原来的图扩大了,所以我们的数组也要开大亿点点,差不多是到n+n×k,其实只要你不MLE你弄成(n+n×k)×10也没有问题,只要自己代码不会出锅就行╮(─▽─)╭

具体实现方式看代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6;

int n, m, k, posx, posy, dis[maxn], vis[maxn];

vector<pair<int,int> > v[maxn];

inline void add_edge(int x, int y, int z){ return (void)v[x].push_back(make_pair(y,z)); }

void Dijkstra(int x){
	memset(dis, 0x3f, sizeof(dis));
	priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > q;
	q.push(make_pair(0,x));
	dis[x] = 0;
	while(q.size()){
		int now = q.top().second, len = q.top().first;
		q.pop();
		if(vis[now]) continue;
		vis[now] = 1;
		for(int i = 0; i < v[now].size(); i ++){
			int to = v[now][i].first, weight = v[now][i].second;
			if(dis[to] > weight + len){
				dis[to] = weight + len;
				q.push(make_pair(dis[to],to));
			}
		}
	}
	return;
}

signed main(){
	scanf("%d%d%d%d%d", &n, &m, &k, &posx, &posy);
	for(int x, y, z; m; m --){
		scanf("%d%d%d", &x, &y, &z);
		add_edge(x, y, z);
		add_edge(y, x, z);
		for(int j = 1; j <= k; j ++){
			add_edge(x+(j-1)*n, y+j*n, 0);
			add_edge(y+(j-1)*n, x+j*n, 0);
			add_edge(x+j*n, y+j*n, z);
			add_edge(y+j*n, x+j*n, z);
		}
	}
	for(int i = 1; i <= k; i ++) add_edge(posy+(i-1)*n, posy+i*n, 0);
	Dijkstra(posx);
	printf("%d", dis[posy+k*n]);
	return 0;
}

做亿点题

衡水某二中集训的考试题

某二中集训的考试题-2_00.png-171.5kB

Solution

二分答案。听我口胡。

代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1000+10;

vector<pair<int,int> >adj[maxn];
vector<pair<int,int> >temp_adj[maxn];

int dis[maxn], n, m, k;

inline void addEdge(int u,int v,int w){
	adj[u].push_back(make_pair(v,w));
	adj[v].push_back(make_pair(u,w));
	return;
}
bool check(int ans){
	for(int i = 1;i<=n;i++){
		temp_adj[i].clear();
	}
	for(int i = 1;i<=n;i++){
		for(int j = 0;j<adj[i].size();j++){
			if(adj[i][j].second <= ans){
				temp_adj[i].push_back(make_pair(adj[i][j].first,0));
			}else{
				temp_adj[i].push_back(make_pair(adj[i][j].first,1));
			}
		}
	}
	memset(dis,0x3f,sizeof(dis));
	deque<int>q;
	dis[1] = 0;
	q.push_back(1);
	while(q.size()){
		int f = q.front();
		q.pop_front();
		for(int i = 0;i<temp_adj[f].size();i++){
			int to = temp_adj[f][i].first,weight = temp_adj[f][i].second;
			if(dis[to] > dis[f] + weight){
				dis[to] = dis[f]+weight;
				if(weight == 0){
					q.push_front(to);
				}else{
					q.push_back(to);
				}
			}
		}
	}
	if(dis[n] > k) return false;
	else return true;
}

int main(){
	cin>>n>>m>>k;
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		addEdge(u,v,w);
	}
	int l = 0,r = 1000000;
	while(l<r){
		int mid = (l+r)/2;
		if(check(mid)) r = mid;
		else l = mid+1;
	}
	if(check(r)) cout<<r<<endl;
	else cout<<-1<<endl;
	return 0;
}

转化为图论模型

主要是看题。

P1854摆花

不是因为我不会动规才转化成最短路的。

Solution

听我口胡。

代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 500;

queue<int> q;

int f, V, comp, ans, ansd, cnt, dis[maxn*maxn], vis[maxn*maxn], pre[maxn*maxn], head[maxn*maxn];
pair<int, int> temp[maxn][maxn];

struct edge{
	int to, length, next;
	inline void push(int x, int y, int z, int cnt){
		to = y, length = z, next = head[x];
		head[x] = cnt;
		return;
	}
	edge(){to = length = next = 0; return;}
}v[maxn*maxn];

inline void add_edge(int x, int y, int z){
	++ cnt;
	v[cnt].push(x,y,z,cnt);
	return;
}

void SPFA(int x){
	memset(pre, 0, sizeof(pre));
	memset(dis, 128, sizeof(dis));
	q.push(x);
	vis[x] = 1, dis[x] = 0;
	while(q.size()) {
	   int now = q.front();
	   q.pop(); 
	   vis[now] = 0;
	   for(int i = head[now]; i; i = v[i].next){
	   	int to = v[i].to, len = v[i].length;
	   	if(dis[to] < dis[now] + len){
	   		dis[to] = dis[now] + len;
	   		pre[to] = now;
	   		if(!vis[to]){
	   			q.push(to);
	   			vis[to] = 1;
	   		}
	      }
	   }
	}
	return ;
}
void print(int x, int high){
	if(!x) return;
	print(pre[x], high-1);
	printf("%d ", x - high*V);
}

signed main(){
	scanf("%d%d",&f,&V);
	for(int i = 1; i <= f; i ++)
		for(int j = 1; j <= V; j ++){
			scanf("%d",&temp[i][j].first);
			temp[i][j].second = ++comp;
		}
	for(int i = 1; i <= V-f+1; i ++) add_edge(0,temp[1][i].second,temp[1][i].first);
	for(int k = 1; k < f; k ++)
		for(int i = k; i <= V-f+k; i ++)
			for(int j = i + 1; j <= V-f+k+1; j ++)
				add_edge(temp[k][i].second,temp[k+1][j].second,temp[k+1][j].first);
	SPFA(0);
	for(int i = f*V-V+1; i <= comp; i ++)
		if(dis[i] > ans) ans = dis[i], ansd = i;
	printf("%d\n", ans);
	print(ansd,f-1);
	return 0;
}

AcWing177电路维修

Solution
建图:如果电线是 \ 就 从右上向左下连边,长为 1,从左上向右下连边,长为 0,意义是改变电线的方向代价是1,不改变就是0,之后跑最短路就行。

本文作者:Vanyun

本文链接:https://www.cnblogs.com/Vanyun/p/13285392.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Vanyun  阅读(134)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
· 用纯.NET开发并制作一个智能桌面机器人:从.NET IoT入门开始
· 一个超经典 WinForm,WPF 卡死问题的终极反思
· ASP.NET Core - 日志记录系统(二)
阅读排行:
· 支付宝事故这事儿,凭什么又是程序员背锅?有没有可能是这样的...
· 在线客服系统 QPS 突破 240/秒,连接数突破 4000,日请求数接近1000万次,.NET 多
· C# 开发工具Visual Studio 介绍
· 在 Windows 10 上实现免密码 SSH 登录
· C#中如何使用异步编程
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.

作曲 : Reol

作词 : Reol

fade away...do over again...

fade away...do over again...

歌い始めの一文字目 いつも迷ってる

歌い始めの一文字目 いつも迷ってる

どうせとりとめのないことだけど

伝わらなきゃもっと意味がない

どうしたってこんなに複雑なのに

どうしたってこんなに複雑なのに

噛み砕いてやらなきゃ伝わらない

ほら結局歌詞なんかどうだっていい

僕の音楽なんかこの世になくたっていいんだよ

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.

目の前 広がる現実世界がまた歪んだ

目の前 広がる現実世界がまた歪んだ

何度リセットしても

僕は僕以外の誰かには生まれ変われない

「そんなの知ってるよ」

気になるあの子の噂話も

シニカル標的は次の速報

麻痺しちゃってるこっからエスケープ

麻痺しちゃってるこっからエスケープ

遠く遠くまで行けるよ

安定なんてない 不安定な世界

安定なんてない 不安定な世界

安定なんてない きっと明日には忘れるよ

fade away...do over again...

fade away...do over again...

そうだ世界はどこかがいつも嘘くさい

そうだ世界はどこかがいつも嘘くさい

綺麗事だけじゃ大事な人たちすら守れない

くだらない 僕らみんなどこか狂ってるみたい

本当のことなんか全部神様も知らない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.