1031 Rinne Loves Graph 求经过k个障碍到达n的最短路 分层图或最短路+dp
链接:https://ac.nowcoder.com/acm/contest/26077/1031
来源:牛客网
题目描述
Island 发生了一场暴乱!现在 Rinne 要和 Setsuna 立马到地上世界去。
众所周知:Island 是有一些奇怪的城镇和道路构成的(题目需要,游戏党勿喷),有些城镇之间用双向道路连接起来了,且每条道路有它自己的距离。但是有一些城镇已经被派兵戒严,虽然主角可以逆天改命强闯,但是为了体验该游戏的平衡性,他们只能穿过不超过 K 次被戒严的城镇。
定义“穿过”:从一个戒严的点出发到达任意一个点,都会使得次数加1
现在他们想从 1 号城镇最快的走到 n 号城镇(即出口),现在他们想让你告诉他们最短需要走多少路。输入描述:
第一行三个整数 n,m,k,分别表示城镇数量,边数量和最多能闯过被戒严的城市的次数。
接下来 n 行,每行一个整数 1 或 0,如果为 1 则表示该城市已被戒严,0 则表示相反。
接下来 m 行,每行三个数 u,v,w,表示在 u 城镇和 v 城镇之间有一条长度为 w 的双向道路。
输出描述:
输出一行一个数字,表示从 1 到 n 的最短路径,如果到达不了的话,请输出 -1。
备注:
2≤n≤800,1≤m≤4000,1≤k≤10,1≤w≤1062 \leq n \leq 800,1 \leq m \leq 4000,1 \leq k \leq 10,1 \leq w \leq 10^62≤n≤800,1≤m≤4000,1≤k≤10,1≤w≤106
保证没有多条道路连接同一对城市,也没有一条道路连向自己。
分析
1.分层图
为每对节点在每一层 建立一条边
1.如果当前节点没有阻塞,就可以直接走到同层另一个节点
2.如果当前节点有阻塞,就需要从当前节点走到下一层的另一个节点
跑最短路
在 给定层数里,取 其中 可以到达终点的层 到达终点的最短路
//-------------------------代码---------------------------- #define int ll const int N = 2e6+10; int n,m,k; int e[N],ne[N],w[N],h[N],idx; int a[N]; int dis[N]; void add(int a,int b,int c) {e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++ ;} bool vis[N]; void dij() { ms(dis,inf);dis[1] = 0; priority_queue<pii,V<pii>,greater<pii>> q; q.push({0,1}); while(q.size()) { auto tmp = q.top();q.pop(); if(vis[tmp.second]) continue; vis[tmp.second] = 1; for(int i = h[tmp.second];~i;i=ne[i]) { int j = e[i]; if(dis[j] > w[i] + tmp.x) { dis[j] = w[i] + tmp.x; q.push({dis[j],j}); } } } } void solve() { cin>>n>>m>>k; ms(h,-1); fo(i,1,n) cin>>a[i]; fo(i,1,m) { int x,y,z;cin>>x>>y>>z; if(!a[x]) { fo(j,0,k) add(x + n * j,y + n * j,z);//当前层 } if(!a[y]) { fo(j,0,k) add(y + n * j,x + n * j,z); } if(a[x]) { fo(j,0,k-1) add(x + n * j,y + n * (j+1),z); } if(a[y]) { fo(j,0,k-1) add(y + n * j,x + n * (j+1),z); } } if(k < 0) {None;rt;} dij(); int ans = inf; fo(i,1,k+1) ans = min(ans,dis[i*n]); if(ans == inf) ans = -1; cout<<ans<<endl; } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------
2.最短路+DP
分析
线性DP是到了某个点 j ,就从前面的点 i 更新过来
这题随着点数的增加,k 会逐渐变大,路程会逐渐变大,当前枚举到的点会变化
所以设状态为 dp [i][ k] 表示当前枚举到 j 点,已经经历了 k 个障碍
由于要跑 到点 n 的最小路径,所以跑dijkstra
当枚举到新的点,更新当前的 k 就可以一路跑到 n 点了。
//-------------------------代码---------------------------- //#define int ll const int N = 1e5+10; int n,m,k; int e[N],h[N],w[N],ne[N],idx; int dis[N][15]; void add(int a,int b,int c) { e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++ ; } struct node { int id,c,k; bool operator<(const node & x) const { return c > x.c; } }; int a[N]; bool vis[N]; void dij() { ms(dis,inf); dis[1][0] = 0; priority_queue<node> q; q.push({1,0,0}); while(q.size()) { auto tmp = q.top();q.pop();; if(vis[tmp.id]) continue; vis[tmp.id] = 1; for(int i = h[tmp.id];~i;i=ne[i]) { int j = e[i];int nowk = a[tmp.id] + tmp.k; if(nowk > k) continue; if(dis[j][nowk] > w[i] + tmp.c) { dis[j][nowk] = w[i] + tmp.c; q.push({j,dis[j][nowk],nowk}); } } } } void solve() { cin>>n>>m>>k; for(int i = 1; i<=n; i++) cin>>a[i]; ms(h,-1); for(int x,y,w,i = 1; i<=m; i++) { cin>>x>>y>>w; add(x,y,w); add(y,x,w); } dij(); int ans = INF; for(int i=0; i<=k; i++) { ans=min(ans,dis[n][i]); } if(ans==INF) puts("-1"); else printf("%d\n",ans); } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------