题解P3953 逛公园

安妮007

2018-02-10 07:18:52

Solution

### 一些思路 这道题运用的思想和最短路计数的思想差不多,但是题目当中还允许不超过最短路长度K的路径,那么就需要在最短路计数上稍作变通才可以: 1. 当然和最短路计数的套路一样,我们需要知道最短路到底是多少才可以判断某条路径是否满足要求,就先用SPFA算法来求一下最短路。 1. 知道最短路之后我们怎么求解满足路径的条数呢?假想一下我们现在从起点开始走——由于我们已经使用SPFA大法来完成了求最短路的使命,我们自然而然的就维护出来了一个d数组(d[i]表示i点到起点的最短距离)这种功能就可以告诉我们当前所走的路是否有“冤枉路”了。解释:“冤枉路”是指从A点至B点所走的路径不是最短路,也就是说一共允许你走K冤枉路的长度,每走一次冤枉路都会消耗这个余额。 1. 通过想象这个过程就能知道前面你的选择会对后面有影响,很自然的想到dp求解,dp(i,j)就表示你当前在i点,还允许你走j容量的“冤枉路”。 1. 那什么时候有无穷多条合法路线呢?首先肯定是在路径里出现了环。(解释:因为简单路线肯定是有限条,无穷的肯定是有环)题目里提到了0边,这就使我们进一步想到0环——如果这个环不是零环,那肯定就不是在最短路上面了(何必绕着一点跑到黑呢!)肯定就会消耗“冤枉路”容量,就不可能是有限条。所以如果有0环出现,判断-1即可。 ### 一些注意事项 - 虽然1号点一定能走到n号点,但是公园中有些点可能无法到达N,所以通过建立反向图的方式反向SPFA来排除那些无法到终点的点,把能到终点的点alive[i]值标为1即可,在dp的时候把alive值为0的点跳过即可 - 本题有多组数据,请大家一定要记得清空数组! ### 一些代码 ```cpp #include<iostream> #include<cstdio> #include<vector> #include<queue> #include<cstring> using namespace std; const int INF=0x7FFFFFFF; struct node { int x; int y; node(int x1,int y1):x(x1),y(y1){} }; int n,m,k,p; vector<node>v[100007]; vector<node>s[100007]; int d[100007]; int ans[100007][57]; bool vis[100007][57]; bool alive[100007]; queue<int>q; queue<int>f; int dfs(int a,int b) { if(b<0) { return 0; } else if(vis[a][b]==1) { return -INF; } else if(ans[a][b]!=-1) { return ans[a][b]; } else { vis[a][b]=1; int key=0; if(a==n) { key++; } for(int i=0;i<v[a].size();i++) { int g=v[a][i].x; int y=v[a][i].y; int u=d[g]-d[a]; if(alive[g]==0) { continue; } int w=dfs(g,b-(y-u)); if(w==-INF) { return -INF; } else { key=(key+w)%p; } } ans[a][b]=key%p; vis[a][b]=0; return key; } } void safe() { f.push(n); alive[n]=1; while(!f.empty()) { int h=f.front(); f.pop(); for(int i=0;i<s[h].size();i++) { int g=s[h][i].x; if(alive[g]==0) { alive[g]=1; f.push(g); } } } return; } void spfa() { q.push(1); d[1]=0; while(!q.empty()) { int h=q.front(); q.pop(); for(int i=0;i<v[h].size();i++) { int g=v[h][i].x; int y=v[h][i].y; if(d[h]+y<d[g]) { d[g]=d[h]+y; q.push(g); } } } return; } int main(){ int t=0; scanf("%d",&t); while(t--) { scanf("%d%d%d%d",&n,&m,&k,&p); for(int i=1;i<=n;i++) { v[i].clear(); alive[i]=0; for(int l=0;l<=k;l++) { ans[i][l]=-1; vis[i][l]=0; } } for(int i=0;i<m;i++) { int a=0,b=0,c=0; scanf("%d%d%d",&a,&b,&c); v[a].push_back(node(b,c)); s[b].push_back(node(a,c)); } for(int i=2;i<=n;i++) { d[i]=INF; } spfa(); safe(); int z=dfs(1,k); if(z==-INF) { printf("%d\n",-1); } else { printf("%d\n",z); } } return 0; } ```