P1462 通往奧格瑞瑪的道路 題解
阿新 • • 發佈:2020-07-05
1.題外話
最近在刷有關圖論,dp的題單~
2.解題意
n個節點,m條雙向邊。每個節點有一個權值\(f[i]\),每個邊有一個邊權(\(edge[i].dis\)),起點編號是1,終點編號是n。讓你求對於每一個b,使得\(1到n\)的最短路小於邊權和小於等於b且使得路徑上經過的最大的點權最小。
3.找思路
很明顯,對於“最大值最小”\(or\)“最小值最大”的問題,考慮二分;
我們二分列舉一個節點限制\(now\),點權f大於now的點不會考慮進路徑,剩下的節點跑一遍最短路;
如果到終點的最短路的dis陣列,也就是最小邊權和小於now,說明當前的這條路徑減少的血量小於當前二分的血量,存在著最多的一次收取的費用的最小值更大的可能
對於二分的題目,一定要明確自己要二分得到的結果,並且要清楚地知道邊界情況的處理方式。如果想錯了可能你的樣例跑出來是對的,但是其他的一些資料會出鍋。
4.\(Code\)
在這裡用的是spfa。也可以用dij
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #define ll long long #define re register #define N 10007 #define inf 2147483646 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,b,num,hea[N],d[N],vis[N],f[N]; int u,v,w; struct edg{ int next,to,dis; }edge[N*20]; inline void add(int from,int to,int dis) { num++; edge[num].dis=dis; edge[num].to=to; edge[num].next=hea[from]; hea[from]=num; } inline bool spfa(int now) { if(now<f[1])return 0; queue<int> q; for(int i=1;i<=n;i++) d[i]=1e9; memset(vis,0,sizeof(vis)); vis[1]=1; d[1]=0; q.push(1); while(!q.empty()) { int k=q.front();q.pop(); vis[k]=0; for(int i=hea[k];i;i=edge[i].next) { int v=edge[i].to; if(d[v]>d[k]+edge[i].dis&&f[v]<=now) { d[v]=d[k]+edge[i].dis; if(!vis[v]) { vis[v]=1; q.push(v); } } } } if(d[n]<b)return 1; return 0; } int main() { n=read(),m=read(),b=read(); for(int i=1;i<=n;i++) f[i]=read(); for(int i=1;i<=m;i++) { u=read(),v=read(),w=read(); add(u,v,w),add(v,u,w); } int l=1,r=1e9+1; if(!spfa(r)) { printf("AFK\n"); return 0; } while(l<=r) { int mid=(l+r)>>1; if(spfa(mid)) r=mid-1; else l=mid+1; } printf("%d",l); return 0; }
完結撒花。有問題可以在評論區指出。