1. 程式人生 > >【NOIP2017】逛公園 最短路+DP

【NOIP2017】逛公園 最短路+DP

() next sizeof truct c++ pre 長度 div 分層圖

誒,去年場上不會處理$0$的環,只拿了$60$有點可惜。

我們先不管邊邊權為$0$的邊。

我們先跑一次最短路,令$dis[u]$表示從$1$至$u$的最短路的長度。

那麽根據題目的要求,從起點走到$u$號點的路徑長度只可能在區間$[dis[u],dis[u]+k]$中。

令$f[i][j]$表示當前從起點走到$i$,行走的路程為$dis[i]+j$的方案數。

不妨發現這個東西可以通過類似分層圖最短路的方式進行更新,然後就直接更新就行了。

然而這一題中有部分點存在邊權為$0$的邊,一旦走入一個$0$環的話采用上述的方法會$TLE$。

於是我們把上面的通過分層圖最短路的更新方式,更換為記憶化搜索,我們在當前搜索的路徑上打上標記,然後若走到之前標記過的點,那麽直接輸出$-1$退出即可。這種方式可以有效避免將無需經過的零環納入考慮範圍內。

(可能是個人寫法的原因),經過$1$的零環需要特判,直接在最短路裏判掉就好了。

然後就沒了。時間復雜度:$O(mk+m log n)$。

 1 #include<bits/stdc++.h>
 2 #define M 100005
 3 using namespace std;
 4 
 5 struct edge{int u,v,next;}e[M*4]={0}; int head[M]={0},head1[M]={0},use=0;
 6 void add(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
7 void add1(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head1[x];head1[x]=use;} 8 int n,m,k,MOD; 9 10 struct node{ 11 int x,dis; 12 node(){x=dis=0;} 13 node(int xx,int ddis){x=xx; dis=ddis;} 14 friend bool operator <(node a,node b){return a.dis>b.dis;}
15 }a[M]; 16 priority_queue<node> q; 17 int vis[M]={0},dis[M]={0},v[M][52]={0},f[M][52]={0},in[M][52]={0}; 18 int dij(){ 19 q.push(node(1,0)); 20 while(!q.empty()){ 21 node U=q.top(); q.pop(); 22 int u=U.x; 23 if(vis[u]) continue; 24 vis[u]=1; dis[u]=U.dis; 25 for(int i=head[u];i;i=e[i].next){ 26 if(dis[u]+e[i].v==0&&e[i].u==1) return 0; 27 if(!vis[e[i].u]) q.push(node(e[i].u,dis[u]+e[i].v)); 28 } 29 } 30 return 1; 31 } 32 33 int dfs(int x,int p){ 34 if(f[x][p]!=-1) return f[x][p]; 35 if(in[x][p]) return -233; 36 int res=0; in[x][p]=1; 37 for(int i=head1[x];i;i=e[i].next){ 38 int v=dis[x]-dis[e[i].u]+p-e[i].v; 39 if(0<=v&&v<=k){ 40 int now=dfs(e[i].u,v); 41 if(now==-233) return -233; 42 res=(res+now)%MOD; 43 } 44 } 45 f[x][p]=res; in[x][p]=0; 46 return res; 47 } 48 49 int Main(){ 50 memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); 51 memset(f,-1,sizeof(f)); memset(e,0,sizeof(e)); 52 memset(head,0,sizeof(head)); memset(head1,0,sizeof(head1)); use=0; 53 memset(in,0,sizeof(in)); 54 memset(v,0,sizeof(v)); 55 scanf("%d%d%d%d",&n,&m,&k,&MOD); 56 for(int i=1;i<=m;i++){ 57 int x,y,z; scanf("%d%d%d",&x,&y,&z); 58 add(x,y,z); 59 add1(y,x,z); 60 } 61 if(dij()==0){printf("-1\n"); return 0;} 62 int ans=0; f[1][0]=1; 63 for(int i=0;i<=k;i++){ 64 int now=dfs(n,i); 65 if(now==-233){ 66 printf("-1\n"); 67 return 0; 68 } 69 ans=(ans+now)%MOD; 70 } 71 printf("%d\n",ans); 72 } 73 74 int main(){ 75 int cas; cin>>cas; 76 while(cas--) Main(); 77 }

【NOIP2017】逛公園 最短路+DP