Codeforces Round #182 (Div. 1) B. Yaroslav and Time(二分+SPFA变形)
题目大意
有 n(3≤n≤100) 个站点,当第一次到达站点 u 的时候会增加寿命 au(1≤au≤103),题目给了 n 个站点的二位空间坐标,每两个站点之间的距离为曼哈顿距离(dis(i, j)=|xi-xj|+|yi-yj|),从站点 i 到达站点 j 需要的时间为 dis(i, j)*d(d 是题目给定的一个常数,且 103≤d≤105)。某个人最开始的时候在站点 1,他想到站点 n 去,问,他最开始至少剩下多少寿命才能活着到达站点 n?
做法分析
很容易想到二分寿命值,但是怎么判断在某个具体的寿命下,能否到达第 n 个站点呢?
注意题目给的数据范围:1≤au≤103 且 103≤d≤105
想到了什么?
对了,如果直接用 SPFA 跑一遍最长路,是不会在路径上出现重复节点的!如果出现重复节点就会形成一个负环,这样肯定不如前面的优!因为就算每到一个节点都增加以下寿命,由于寿命增加的肯定比在路上消耗的小,所以寿命是随着经过的节点的数量而递减的!
那么,每当遇见 f[u]-dis(i, u)*d+A[i]>f[i] (其中:f[u] 表示到达 u 节点剩余的最大寿命值,A[i] 表示第一次进入 i 节点增加的寿命值)的时候,肯定是从另一条路径到达的 i 节点。那么,我跑一遍 SPFA,看能不能到达 n 节点,这样,就判断出来了
参考代码
1 #include <iostream> 2 #include <cstring> 3 #include <cmath> 4 #include <cstdio> 5 #include <queue> 6 7 using namespace std; 8 9 typedef long long LL; 10 const int N=106; 11 12 LL d, A[N], f[N]; 13 bool vs[N]; 14 int X[N], Y[N], n; 15 queue <int> q; 16 17 inline LL dis(int a, int b) 18 { 19 return abs(X[a]-X[b])+abs(Y[a]-Y[b]); 20 } 21 22 bool judge(LL init) 23 { 24 memset(f, -1, sizeof f); 25 while(!q.empty()) q.pop(); 26 memset(vs, 0, sizeof vs); 27 q.push(1), f[1]=init, vs[1]=1; 28 while(!q.empty()) 29 { 30 int u=q.front(); 31 q.pop(), vs[u]=0; 32 for(int i=1; i<=n; i++) 33 { 34 if(dis(i, u)*d>f[u] || i==u) continue; 35 if(f[u]-dis(i, u)*d+A[i]>f[i]) 36 { 37 f[i]=f[u]-dis(i, u)*d+A[i]; 38 if(!vs[i]) q.push(i), vs[i]=1; 39 } 40 } 41 } 42 if(f[n]==-1) return false; 43 return true; 44 } 45 46 int main() 47 { 48 scanf("%d%I64d", &n, &d); 49 for(int i=2; i<n; i++) scanf("%I64d", &A[i]); 50 for(int i=1; i<=n; i++) scanf("%d%d", &X[i], &Y[i]); 51 LL L=0, R=((1LL)<<60); 52 while(L<R) 53 { 54 LL mid=(L+R)>>1; 55 if(judge(mid)) R=mid; 56 else L=mid+1; 57 } 58 if(!judge(L)) L++; 59 printf("%I64d\n", L); 60 return 0; 61 }
题目链接 & AC通道
Codeforces Round #182 (Div. 1) B. Yaroslav and Time