題解 P7192 【[COCI2007-2008#6] GEORGE】
阿新 • • 發佈:2021-06-23
不扯閒話了
進入正題
偉大的 \(cpp\) 告訴我們,拿到題目一定要看一眼資料範圍。於是我們先來看一下點數和邊數。發現\(n\le10^3\) ,\(m\le10^4\),基本就是一個稠密圖,而且點數不多,於是我們可以選擇使用鄰接矩陣來存圖。
然後來考慮 \(Luka\) 應該怎麼走。顯然應該讓 \(T\) 先生先走,於是我們考慮記錄每條路的封鎖時間。
int tt=0; F(i,1,g-1) { t[c[i]][c[i+1]]=t[c[i+1]][c[i]]=tt; //考慮其是雙向邊,應當將其反向再記錄一遍 //t[i][j]表示從路口i到路口j這一段路的管制開始時刻 tt+=f[c[i]][c[i+1]];//tt記錄當前時刻 }
然後我們用 \(Dijkstra\) 跑一遍最短路即可。
細節見程式碼(稍微用了一點點巨集):
#include<bits/stdc++.h> #define rd(n) scanf("%d",&n); #define F(i,a,b) for(register int i=a;i<=b;i++) using namespace std; const int N=1e3+10; int n,m,a,b,k,g,f[N][N]; int c[N],t[N][N],dis[N]; bool vis[N]; struct P { int d,t;//d表示節點的編號,t表示到達該點的時間 bool operator <(const P&a)const { return t>a.t; } }; int drive(int u,int v) { if(dis[u]>=t[u][v]&&dis[u]<=t[u][v]+f[u][v]-1) { /*當到達時間恰好為管制時間 最小時間為T先生到達時刻加上兩倍過路時間 (T先生過路的時間加上Luka過路的時間,恰好為兩倍。) */ return (f[u][v]<<1)+t[u][v]; } return dis[u]+f[u][v];//否則就直接加過路需要的時間 } void Dij() { memset(dis,0x3f,sizeof(dis));//初始化為較大值 dis[a]=k; priority_queue<P>q;//堆優化 q.push({a,k});//從點a開始,時刻為k while(!q.empty()) { P t=q.top(); q.pop(); if(vis[t.d])continue; vis[t.d]=1; F(i,1,n) { if(f[t.d][i]) { //t.d與i之間存在一條邊 int pos=drive(t.d,i);//計算到達i點的最短時間 if(dis[i]>pos) {//鬆弛操作 dis[i]=pos; q.push({i,dis[i]}); } } } } } int main() { rd(n)rd(m)rd(a)rd(b)rd(k)rd(g) F(i,1,g)rd(c[i])//c是過路所需時間 int u,v,w; F(i,1,m) {//鄰接矩陣存圖 rd(u)rd(v)rd(w) f[u][v]=w;//雙向邊 f[v][u]=w; } int tt=0; F(i,1,g-1) { t[c[i]][c[i+1]]=t[c[i+1]][c[i]]=tt; //考慮其是雙向邊,應當將其反向再記錄一遍 //t[i][j]表示從路口i到路口j這一段路的管制開始時刻 tt+=f[c[i]][c[i+1]];//tt記錄當前時刻 } Dij(); printf("%d",dis[b]-k);//開車時間應當減去k return 0; }