瑪麗卡 題解

瑪麗卡

洛谷P1186 瑪麗卡node

題目簡述

不得不吐槽一下,這道題的題目描述真的有毒,讀完題滿臉懵QAQ(也可能由於我菜)c++

就根據我本身的理解來轉述一下題意吧(可能也不清楚,輕噴啊)數組

給定\(N\)個城市和\(M\)條道路,對於每條路,給定鏈接的兩個城市編號以及經過這條路所花費的時間,再告訴你在某一時刻有一條路可能會堵車優化

要求找到一個\(t\)知足:spa

  1. \(t\)時刻內,不管哪一條道路堵車(即不能走),你都能找到一條從\(1\)\(N\)的路徑,且這條路徑所花費的總時長必定\(≤t\).net

  2. 可是任意小於\(t\)的值\(t'\),都存在至少一種狀況使得如有一條路堵車,那麼則沒法找到一條從\(1\)\(N\)的路徑,知足這條路徑所花費的總時長必定\(≤t'\)code

可是這題仍是良心的,它不卡SPFA!!blog

仍是要提醒一下,這道題最後一個點的\(M\)\(2*10^5\),因此數組要開大一點get


解題思路

仍是有點繞?那咱們來分析一下樣例吧:it

直接求最短路確定是:1->2->5,最短期花費總和則是:8+1=9

可是根據題意,可能會有一條路堵車:假設2->5這條路堵車了,那麼在9分鐘內咱們顯然沒法找到一條從\(1\)\(N\)的路徑,因此9這個答案是錯誤的

因此直接跑最短路是錯誤作法,接下來來說解一下正解

  • 模擬堵車(刪邊)

由於一旦堵車那麼那一條路咱們就不能走,因此堵車=不能走=刪邊

沒有直接的解題思路,那咱們就先來手模一遍樣例的刪邊操做:

刪1->2(8):最短期21

刪1->4(10):最短期9

刪2->4(10):最短期9

刪2->5(1):最短期27

刪2->3(9):最短期9

刪3->5(10):最短期9

刪3->4(7):最短期9

找到了嗎?樣例輸出的\(27\)就在咱們上面的刪邊操做裏面

好像有點思路了:咱們模擬依次刪除每一條邊,而後跑一邊最短路找到當前對應的最短期,最後在全部最短期中找到最大值,就是咱們的答案

  • 思路優化

將上面的初步思路實現爲代碼,咱們只能獲得50pts~80pts(\(Dijkstra\) 50pts,\(SPFA\) 80pts),其他的點都是TLE

獲得一大部分分可是超時了,說明咱們的思路缺乏優化

再來分析樣例,咱們從上面的刪邊操做模擬就會發現,只有刪邊\(1->2\)\(2->5\)最短期纔會發生變化,刪其餘邊獲得的結果依舊是最開始的最短路徑

爲何呢?由於刪其餘邊很明顯不會影響到原來的最短路徑啊!

因此咱們刪邊只須要在原始的最短路徑上進行便可


代碼Code

  • 先上AC代碼(思路優化版)
#include <bits/stdc++.h>
using namespace std;
int n,m,u,v,w,tot,ans,summ,sum[5000010];
int dis[5000010],vis[5000010],pre[5000010],head[5000010];
priority_queue<pair<int,int> > shan;

struct node {
	int to,net,val;
} e[5000010];

inline void add(int u,int v,int w) {
	e[++tot].to=v;
	e[tot].val=w;
	e[tot].net=head[u];
	head[u]=tot;
}

inline void dijkstra(int xx,int yy) {
	for(register int i=1;i<=n;i++) {
		vis[i]=0;
		pre[i]=0;
		dis[i]=20050206;
	}
	dis[1]=0;
	shan.push(make_pair(0,1));
	while(!shan.empty()) {
		int x=shan.top().second;
		shan.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(x==xx&&v==yy) continue;
			if(dis[v]>dis[x]+e[i].val) {
				dis[v]=dis[x]+e[i].val;
				pre[v]=x;
				shan.push(make_pair(-dis[v],v));
			}
		}
	}
	
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	dijkstra(0,0);
	int k=n;
	while(k) {
		sum[++summ]=k;
		k=pre[k];
	}
	for(register int i=summ;i>1;i--) {
		dijkstra(sum[i],sum[i-1]);
		ans=max(ans,dis[n]);
	}
	printf("%d",ans);
	return 0;
}
  • 再來基礎思路版的未AC代碼
#include <bits/stdc++.h>
using namespace std;
int n,m,tot,ans,u[50010],v[50010],w[50010];
int dis[50010],vis[50010],head[50010];

struct node {
	int to,net,val;
} e[50010];

inline void add(int u,int v,int w) {
	e[++tot].to=v;
	e[tot].net=head[u];
	e[tot].val=w;
	head[u]=tot;
}

inline void spfa(int xx,int yy) {
	queue<int> shan;
	for(register int i=1;i<=n;i++) {
		vis[i]=0;
		dis[i]=20050206;
	} 
	dis[1]=0;
	vis[1]=1;
	shan.push(1);
	while(!shan.empty()) {
		int x=shan.front();
		shan.pop();
		vis[x]=0;
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(x==xx&&v==yy) continue;
			if(dis[v]>dis[x]+e[i].val) {
				dis[v]=dis[x]+e[i].val;
				if(vis[v]==0) {
					shan.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&u[i],&v[i],&w[i]);
		add(u[i],v[i],w[i]);
		add(v[i],u[i],w[i]);
	}
	for(register int i=1;i<=m;i++) {
		spfa(u[i],v[i]);
		if(dis[n]!=20050206) ans=max(ans,dis[n]);
	}
	printf("%d",ans);
	return 0;
}

最後,感謝一下ZJY大佬提供的思路優化

相關文章
相關標籤/搜索