最短路問題專題(Luogu訓練場及各種總結)

P1462 通往奧格瑞瑪的道路

題目:找最短路徑中點權的最大值的最小值

最大值的最小值,最小值的最大值,一般這種題目都會想到二分答案去解決。

所以我們先把所有點權排個序(二分需滿足有序性),然後二分點權,跑一遍最短路確認該方案是否可行(走的路徑時,邊權損失的HP要儘量的小,這樣纔可能到達最終的點,維護答案即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define int long long
#define ll int
const int maxn = 1e6+5;
struct Edge{
    int u,v,nxt,c;
}edge[maxn<<1];
struct Node{
    int u,dis;
    bool operator <(const Node &h)const{
        return dis > h.dis;
    }
};
int head[maxn],tot;
inline void init(){
    memset(head,-1,sizeof(head));
    tot = 0;
}
inline void addedge(int u,int v,int c){
    ++tot;
    edge[tot] = {u,v,head[u],c};
    head[u] = tot;
}
int f[maxn],ff[maxn];
int dis[maxn];
bool vis[maxn];
bool dijiastra(int u,int v,int fm,int hp){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[u]  = 0;
    if(fm < ff[u])   return false;


    priority_queue<Node> pq;    pq.push(Node{u,0});
    while(!pq.empty()){
        Node x = pq.top();  pq.pop();
        if(vis[x.u])    continue;
        vis[x.u] = true;
        for(int i = head[x.u]; ~i; i = edge[i].nxt){
            Edge &e = edge[i];
            if(ff[e.v] > fm) continue;
            if(dis[e.v] > dis[x.u] + e.c){
                dis[e.v] = dis[x.u] + e.c;
                pq.push(Node{e.v,dis[e.v]});
            }
        }
    }
    return dis[v] <= hp;
}
signed main()
{
    init();
    int n,m,b;  cin >> n >> m >> b;
    for(int i = 1; i <= n; ++i){
        cin >> f[i];    ff[i] = f[i];
    }
    sort(f+1,f+n+1);


    for(int i = 0; i < m; ++i){
        int u,v,c;  cin >> u >> v >> c;
        addedge(u,v,c);
        addedge(v,u,c);
    }
    if(!dijiastra(1,n,f[n],b)){
        cout << "AFK" << endl;
        return 0;
    }
    int l = 1,r = n;
    int ans = 1e9;
    while(l <= r){
        int mid = l + r >> 1;
        if(dijiastra(1,n,f[mid],b)){
            ans = f[mid];
            r = mid-1;;
        }
        else l = mid+1;
    }
    cout << ans << endl;
    return 0;
}

題意:
在一個神奇的小鎮上有着一個特別的電車網絡,它由一些路口和軌道組成,每個路口都連接着若干個軌道,每個軌道都通向一個路口(不排除有的觀光軌道轉一圈後返回路口的可能)。在每個路口,都有一個開關決定着出去的軌道,每個開關都有一個默認的狀態,每輛電車行駛到路口之後,只能從開關所指向的軌道出去,如果電車司機想走另一個軌道,他就必須下車切換開關的狀態。
爲了行駛向目標地點,電車司機不得不經常下車來切換開關,於是,他們想請你寫一個程序,計算一輛從路口A到路口B最少需要下車切換幾次開關。

轉換成最短路來考慮問題,如果下來需要切換開關,那麼u->v相當於邊權就賦予了1,反之邊權就爲0,跑一遍最短路就可以得到答案了


#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
struct Edge{
    int u,v,nxt;
}edge[maxn];
struct Node{
    int u,dis;
    bool operator <(const Node &h)const{
        return dis > h.dis;
    }
};
int head[maxn],tot;
inline void init(){
    memset(head,-1,sizeof(head));
    tot = 0;
}
inline void addedge(int u,int v){
    ++tot;
    edge[tot] = {u,v,head[u]};
    head[u] = tot;
}
int state[maxn];    // 每個點的開關狀態
int dis[maxn];  bool vis[maxn];
const int inf = 0x3f3f3f3f;
void dijiastra(int s,int t){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s] = 0;
    priority_queue<Node> pq;    pq.push(Node{s,0});
    while(!pq.empty()){
        Node x = pq.top();  pq.pop();
        if(vis[x.u])    continue;   vis[x.u] = true;
        for(int i = head[x.u]; ~i; i = edge[i].nxt){
            Edge &e = edge[i];
            int tag = (state[x.u]==e.v? 0 : 1);
            if(dis[e.v] > dis[x.u] + tag){
                dis[e.v] = dis[x.u] + tag;
                pq.push(Node{e.v,dis[e.v]});
            }
        }
    }
    if(dis[t]==inf) cout << -1 << endl;
    else cout << dis[t] << endl;
}
int main()
{
    init();
    int n,a,b;  cin >> n >> a >> b;
    for(int i = 1; i <= n; ++i){
        int k;  cin >> k;
        for(int j = 0; j < k; ++j){
            int x;  cin >> x;
            if(!j)  state[i] = x;
            addedge(i,x);
        }
    }
    dijiastra(a,b);
    return 0;
}

最短路Floyed

(看了一波別人大佬的題解) 對floyed有了新的認識,原來覺得floyed由於O(n3)O(n^3)的複雜度,覺得適用面一直挺窄的,但其實是自己對其的理解程度還不夠。
這個算法的主要思路,就是通過其他的點進行中轉來求的兩點之間的最短路。因爲我們知道,兩點之間有多條路,如果換一條路可以縮短距離的話,就更新最短距離。而它最本質的思想,就是用其他的點進行中轉,從而達到求出最短路的目的。

那麼,如何進行中轉呢?兩點之間可以由一個點作爲中轉點更新最短路徑,也可以通過多個點更新最短路徑。

結合代碼:

for(k=1;k<=n;k++)

    for(i=1;i<=n;i++)

        for(j=1;j<=n;j++)

            if(e[i][j]>e[i][k]+e[k][j])

                 e[i][j]=e[i][k]+e[k][j];

//核心代碼,僅僅只有5行
這段代碼的基本思想就是:最開始只允許經過1號頂點進行中轉,接下來只允許經過1和2號頂點進行中轉……允許經過1~n號所有頂點進行中轉,求任意兩點之間的最短路程。用一句話概括就是:從i號頂點到j號頂點只經過前k號點的最短路程。

P1119 災後重建

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2e2+5;
int day[maxn];
int dp[205][205];
inline void floyed(int k,int n){
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < n; ++j){
			if(dp[i][j] > dp[i][k] + dp[j][k]){
				dp[i][j] = dp[j][i] = dp[i][k] + dp[j][k];
			}
		}
	}
}
const int inf = 0x3f3f3f3f;
int main()
{
	memset(dp,0x3f,sizeof(dp));
	int n,m;	cin >> n >> m;
	for(int i = 0; i < n; ++i)	cin >> day[i];
	for(int i = 0; i < m; ++i){
		int u,v,w;	cin >> u >> v >> w;
		dp[u][v] = w;	dp[v][u] = w;
	}
	for(int i = 0; i < n; ++i)
		dp[i][i] = 0;
	int now = 0;
	int Q;	cin >> Q;
	for(int i = 0; i < Q; ++i){
		int x,y,t;	cin >> x >> y >> t;
		while(day[now] <= t && now < n){
			floyed(now,n);	++now;
		}
		if(day[x] > t || day[y] > t){
			cout << -1 << endl;
		}else{
			if(dp[x][y]==inf){
				cout << -1 << endl;
			}
			else cout << dp[x][y] << endl;
		}

	}
	return 0;
}

2019南昌網絡邀請賽B題(這裏採用了虛擬結點連法)

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e6+1e5;
int v,e,ss,k,c;
struct Edge{
	int u,v,nxt,w;
}edge[maxn];
struct Node{
	int u,dis;
	bool operator < (const Node &h)const{
		return dis > h.dis;
	}
};
int tot,head[1005];
inline void addedge(int u,int v,int w){
	++tot;
	edge[tot] = {u,v,head[u],w};
	head[u] = tot;
}
int dis[1005];
bool vis[1005];
int heroMax,xfdd,xfdw[1005],tot2;

void dijiastra(int s,int n){
	memset(dis,0x3f,sizeof(dis));	memset(vis,0,sizeof(vis));
	dis[s] = 0;

	priority_queue<Node> pq;	pq.push(Node{s,0});
	while(!pq.empty()){
		Node x = pq.top();	pq.pop();
		if(vis[x.u])	continue;
		vis[x.u] = true;
		for(int i = head[x.u]; ~i; i = edge[i].nxt){
			Edge & e = edge[i];
			if(!vis[e.v] && dis[e.v] > x.dis + e.w){
				dis[e.v] = x.dis + e.w;
				pq.push(Node{e.v,dis[e.v]});
			}
		}
	}

}
inline void init(){
	memset(head,-1,sizeof(head));
	heroMax = 0;
	xfdd = 0;
	tot = 0;
}
int main()
{
	int T;	scanf("%d",&T);
	while(T--){
		init();
		scanf("%d%d%d%d%d",&v,&e,&ss,&k,&c);
		int nn = v+1;


		for(int i = 0; i < k; ++i){
			int x;	scanf("%d",&x);
			xfdw[i] = x;
			addedge(nn,x,0);
		}

		for(int i = 0; i < e; ++i){
			int u,v,w;	scanf("%d%d%d",&u,&v,&w);
			addedge(u,v,w);
			addedge(v,u,w);
		}

		dijiastra(nn,v);
		for(int j = 1; j <= v; ++j)	xfdd = max(xfdd,dis[j]);


		dijiastra(ss,v);
		for(int i = 1; i <= v; ++i){
			heroMax = max(heroMax,dis[i]);
		}


		if(heroMax <= xfdd*c){
			printf("%d\n",heroMax);
		}
		else printf("%d\n",xfdd);

	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章