题目大意:N个点M条无向边,每个点有可能有红绿灯,或者是加油站,或者单单是一个点。红绿灯太多会让人烦,太久不加油车子就会开不动,问最多通过K次红绿灯,从“start”点到“end”点的最少花费是多少。
思路:只能最多通过K次红绿灯,可以依据这个建分层图。f[ i ][ j ]为在已经通过i次红绿灯后,在j点时的最小花费。这只是总体的思路,具体是实现起来还是有其他一些小问题。
题目中有一个limit,表示从一个加油站到另一个加油站不能行驶超过这么远。仔细想想,其实那些不是加油站的点对我们来说没什么意义。经过简单的处理即可把他们处理掉。以每个加油站为起点进行SPFA,计算出这个节点到其他节点的距离和经过了多少红绿灯。把这些信息加入新图中,再从起点到终点跑一次SPFA就可以得到答案。
另外,红绿灯的等待时间取得是数学期望,简单画图像可得time = (red * red) / (2 * (red + green))
CODE:
#include <map> #include <queue> #include <cstdio> #include <string> #include <iomanip> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define INF 0x7f7f7f7f using namespace std; map<string,int> G; map<int,int> gas_num; double f[11][MAX]; bool v[11][MAX]; struct Complex{ int pos,step; Complex(int _step,int _pos):pos(_pos),step(_step) {} Complex() {} bool operator <(const Complex& a)const { return f[step][pos] > f[a.step][a.pos]; } }; int points,edges,k,limit,cost; int st,ed; int total_gas; double expection[MAX]; bool stop[MAX],is_gas[MAX]; int head[MAX],total; int next[MAX],aim[MAX]; double length[MAX]; int _head[MAX],_total; int _next[MAX],_aim[MAX]; int lights[MAX]; double _length[MAX]; string temp; inline void Add(int x,int y,double len); inline void _Add(int x,int y,double _len,int l); inline void _SPFA(int start); void SPFA(); int main() { cin >> points >> edges >> k >> limit >> cost; for(int x,y,i = 1;i <= points; ++i) { cin >> temp; G[temp] = i; if(temp == "start") st = i,is_gas[i] = true; if(temp == "end") ed = i,is_gas[i] = true; if(temp.find("gas") != string::npos) is_gas[i] = true; scanf("%d%d",&x,&y); if(x) { expection[i] = (double)(x * x) / (2.0 * (x + y)); stop[i] = true; } } for(int x,y,len,i = 1;i <= edges; ++i) { cin >> temp; x = G[temp]; cin >> temp; y = G[temp]; cin >> temp >> len; Add(x,y,(double)len + expection[y]),Add(y,x,(double)len + expection[x]); } for(int i = 1;i <= points; ++i) if(is_gas[i]) _SPFA(i); SPFA(); double ans = INF; for(int i = 0;i <= k; ++i) ans = min(ans,f[i][ed]); cout << fixed << setprecision(3) << ans - cost; return 0; } inline void Add(int x,int y,double len) { next[++total] = head[x]; aim[total] = y; length[total] = len; head[x] = total; } inline void _Add(int x,int y,double len,int l) { _next[++_total] = _head[x]; _aim[_total] = y; _length[_total] = len; lights[_total] = l; _head[x] = _total; } inline void _SPFA(int start) { static priority_queue<Complex> q; while(!q.empty()) q.pop(); memset(f,0x43,sizeof(f)); memset(v,false,sizeof(v)); f[0][start] = 0; q.push(Complex(0,start)); while(!q.empty()) { Complex temp = q.top(); q.pop(); int x = temp.pos,step = temp.step; v[step][x] = false; for(int i = head[x];i;i = next[i]) { bool detla = stop[aim[i]]; if(step + detla <= k && f[step + detla][aim[i]] > f[step][x] + length[i]) { f[step + detla][aim[i]] = f[step][x] + length[i]; if(!v[step + detla][aim[i]]) { v[step + detla][aim[i]] = true; q.push(Complex(step + detla,aim[i])); } } } } for(int i = 0;i <= k; ++i) for(int j = 1;j <= points; ++j) if(j != start && f[i][j] <= limit && is_gas[j]) _Add(start,j,f[i][j] + cost,i); } void SPFA() { static priority_queue<Complex> q; memset(f,0x43,sizeof(f)); memset(v,false,sizeof(v)); f[0][st] = 0; q.push(Complex(0,st)); while(!q.empty()) { Complex temp = q.top(); q.pop(); int x = temp.pos,step = temp.step; v[step][x] = false; for(int i = _head[x];i;i = _next[i]) if(step + lights[i] <=k && f[step + lights[i]][_aim[i]] > f[step][x] + _length[i]) { f[step + lights[i]][_aim[i]] = f[step][x] + _length[i]; if(!v[step + lights[i]][_aim[i]]) { v[step + lights[i]][_aim[i]] = true; q.push(Complex(step + lights[i],_aim[i])); } } } }