題解 P2176 [USACO11DEC]RoadBlock S / [USACO14FEB]Roadblock G/S
阿新 • • 發佈:2020-08-08
P2176 [USACO11DEC]RoadBlock S / [USACO14FEB]Roadblock G/S
好題,這道題有許多值得記錄的細節。
-
在鏈式前向星中,記錄鄰接表邊權的編號。
這讓我對鄰接表結構有了更深的理解,原本以為,變數cnt的意義只是一個計數器,而當建圖的過程完成後,cnt的歷史記錄是不可查的,也就是不可記憶化。
可是事實並不是如此,若在題目中明確指出,兩點之間不存在重邊時,cnt就可以被記憶化。//存在重邊應該也可以,設想應該要麻煩很多。
思路:已知起點和終點,因為兩點之間不存在重邊,所以已知兩點的編號,就可以找到對應的邊的相關屬性。所以只要在存邊時,有意的記錄下來點與cnt的關係,這個問題就解決了。
PS:題目中,我用的是二維陣列,是因為在這道題中,空間範圍允許,二維陣列最方便。其實最好的,是用pair或者自寫結構體。 -
對於前驅的迴圈操作。
for(int i=n;i;i=pre[i])//前驅的迴圈
相對於寫遞迴,這樣寫更清爽,而且應該也可以省空間。
值得注意的是,這種寫法對於整個圖來說的反向的。 -
關於陣列的初始化
因為這個原因,這道題調了好久。
以前總是認為,在全域性中開陣列是預設0。就懶的初始化。
現在看,還是最好都初始化一下,因為有時需要進行多次操作。
回到這題。
思路:若擴大一條邊,能使最短路增加,這條邊一定在原最短路上。所以列舉最短路上的邊,逐一擴大,然後重新跑最短路,然後得到增量。
PS:千萬不要重新建圖,耗不起。
#include <bits/stdc++.h> #define MAXN 200000 #define INF 0x3f3f3f3f int n,m,cnt=0,ans=0; bool flag=1; int adj[MAXN],dis[MAXN],vis[MAXN],pre[MAXN]; int linker[100][100]; struct EDGE{int to,nxt,val;} e[MAXN]; struct node { int pos,dis; bool operator < (const node &x) const {return x.dis<dis;} }; void addedge(int u,int v,int w) { e[++cnt].to=v; e[cnt].nxt=adj[u]; e[cnt].val=w; adj[u]=cnt; linker[u][v]=cnt; } std::priority_queue < node > q; void Dijkstra() { std::memset(vis,0,sizeof(vis)); //vis陣列清零 for(int i=1;i<=n;++i) dis[i]=INF; //dis初始化 dis[1]=0; q.push((node){1,0}); while(!q.empty()) { int u=q.top().pos; q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=adj[u];i;i=e[i].nxt) { int v=e[i].to; if(dis[v]>dis[u]+e[i].val) { dis[v]=dis[u]+e[i].val; if(flag) pre[v]=u; if(!vis[v]) q.push((node){v,dis[v]}); } } } } int main() { std::scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) pre[i]=0;//前驅陣列初始化 for(int i=1;i<=m;++i) { int u,v,w; std::scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } Dijkstra(); int minn=dis[n]; flag=0; for(int i=n;i;i=pre[i])//前驅的迴圈 { int x=linker[i][pre[i]],y=linker[pre[i]][i]; e[x].val*=2;e[y].val*=2; Dijkstra(); if(dis[n]!=INF) ans=std::max(ans,dis[n]-minn); e[x].val/=2;e[y].val/=2; } std::printf("%d",ans); return 0; }