变化,给定每个灯初态,时间。
思路:开始就想到直接DP,方程dp[k]=dp[i]+distance[i][k]+waittime(i,k),于是天真得BFS敲了这个,
SB啊!有些点松弛了,之后它又被松弛,它便还可以松弛别人啊!否则世界上最短路O(n)的算法就诞生了!
所以,一旦某个点被松弛了,它便可以再入队,啊哈?这不就是SPFA吗!!我去,这次是对SPFA更加理解了,
自己想法竟然遇上经典算法了。其本质就是DP,状态的转移,初期放的错误是:没有所有点都对k松弛,而k已经出队
(对别人松弛过了)。
这题较麻烦的地方是计算等待的时间,细节模拟,我是先算出当前时间,路口的状态,再讨论下接下来等待的
时间。
未1A:
复制差不多代码的时候2个数据没有改,导致错误。打印路劲的时候开始用栈(因为记录是逆序的)。导致爆内存,
以后尽量不要用栈记录!
改进:其实写等待函数可以分俩个函数,每个点当前状态一个,加一个算时间的函数。那样代码就减少很多,
并且也清晰很多。
#include<iostream> #include<queue> #include<cstdio> using namespace std; const int inf=0x3f3f3f3f; struct nodes { int remain; char initial; int tb; int tp; }; nodes node[310]; int n,m;int s,l; int times=0; int e[50000][3];int head[310];int nume=0; void inline addedge(int from,int to, int w) { e[nume][0]=to;e[nume][1]=head[from];head[from]=nume; e[nume++][2]=w; e[nume][0]=from;e[nume][1]=head[to];head[to]=nume; e[nume++][2]=w; } int dp[310]; void readin() { cin>>s>>l>>n>>m; for(int i=1;i<=n;i++) { head[i]=-1; dp[i]=inf; cin>>node[i].initial>>node[i].remain>>node[i].tb>>node[i].tp; } int from,to,w; for(int i=0;i<m;i++) { cin>>from>>to>>w; addedge(from,to,w); } } char inline change(char s) //变色 { if(s=='B')return 'P'; return 'B'; } int gettime(int nowt,int u,int v) //计算等待时间 { int reu=0,rev=0; //模拟出当前灯还剩余多少时间 int nowu=nowt-node[u].remain; //先减去开始的剩余时间 char cv=node[v].initial,cu=node[u].initial;//cu,cv模拟出当前灯 if(nowu==0) //干好 { if(node[u].initial=='B'){cu='p';reu=node[u].tp;} else {cu='B';reu=node[u].tb;} } else if(nowu<0) //剩余时间还有 { reu=node[u].remain-nowt; } else { int re=nowu%(node[u].tb+node[u].tp);//多余 if(re==0)cu=change(cu); //讨论略繁琐 else if(cu=='B') { if(re<node[u].tp) cu=change(cu); } else if(cu=='P') { if(re<node[u].tb) cu=change(cu); } if(cu=='P') { reu=(node[u].tb+node[u].tp)-re-node[u].tb; if(node[u].initial=='P')reu+=node[u].tb; } else if(cu=='B') { reu=(node[u].tb+node[u].tp)-re-node[u].tp; if(node[u].initial=='B')reu+=node[u].tp; } } int nowv=nowt-node[v].remain; if(nowv==0) { if(node[v].initial=='B'){cv='p';rev=node[v].tp;} else {cv='B';rev=node[v].tb;} } else if(nowv<0) { rev=-nowv; } else { int re=nowv%(node[v].tb+node[v].tp); if(re==0)cv=change(cv); else if(cv=='B') { if(re<node[v].tp) cv=change(cv); } else if(cv=='P') { if(re<node[v].tb) cv=change(cv); } if(cv=='P') { rev=(node[v].tb+node[v].tp)-re-node[v].tb; if(node[v].initial=='P')rev+=node[v].tb; } else if(cv=='B') { rev=(node[v].tb+node[v].tp)-re-node[v].tp; if(node[v].initial=='B')rev+=node[v].tp; } } if(cu==cv)return 0; else { if(reu>rev) return rev; if(reu<rev) return reu; else { cu=change(cu); cv=change(cv); if(cu=='B') { if(node[u].tb<node[v].tp) return reu+node[u].tb; else if(node[u].tb>node[v].tp) return reu+node[v].tp; else { if(node[u].tp<node[v].tb) return reu+node[u].tb+node[u].tp; else if(node[u].tp>node[v].tb) return reu+node[v].tp+node[v].tb; else return inf; } } else if(cu=='P') { if(node[u].tp<node[v].tb) return reu+node[u].tp; else if(node[u].tp>node[v].tb) return reu+node[v].tb; else { if(node[u].tb<node[v].tp) return reu+node[u].tp+node[u].tb; else if(node[u].tb>node[v].tp) return reu+node[v].tb+node[v].tp; else return inf; } } } } } int vis[310]; int fa[310]; int way[310]; queue<int>q; int main() { readin(); q.push(s); dp[s]=0; while(!q.empty()) //spfa { int cur=q.front(); q.pop(); vis[cur]=0; for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; int getv=dp[cur]+e[i][2]; getv+=gettime(dp[cur],cur,v); if(vis[v]==0&&getv<dp[v]) { q.push(v); vis[v]=1; } if(getv<inf&&getv<dp[v]) { dp[v]=getv; fa[v]=cur; //跟新时候记录父节点,不可记录子节点法,容易想到反例。 } } } if(dp[l]>=inf){cout<<0<<endl;return 0;} //无解 cout<<dp[l]<<endl; int i=l; int num=0; while(i!=s) { way[num++]=i; i=fa[i]; } way[num]=i; for(int j=num;j>=0;j--) { if(j==0)printf("%d ",way[j]); else printf("%d ",way[j]); } return 0; }