写在前面
这篇写到的做法并不是真正的分层图,只是一个假的低配版算法。
【我这么菜哪里会分层图呢?(╯▔皿▔)╯
动态规划。
问题
【既然不是真的分层图,那么就不说什么是分层图了。
该算法能解决的问题:
在一张带权图上面,有k次机会修改边权【非同一条边,求从起点到终点的最短路。
假如会分层图的话,就是一个板板。
思考
那么不会的话怎么办?
总是会有一些巨巨给予菜鸡福音。
不知是哪位神犇想出来了简单的动态规划,解决了这个问题。
解法
首先确认,点数 * 修改次数 能够存成一个二维数组。
然后,定义 dp[ i ][ j ]表示从起点到 j 的最短路【修改 i 次
观察定义我们可以发现:
对于每一种情况,我们只需要跟普通的dp一样,分成选与不选两种情况讨论。
唯一的不同就是,一般的dp是在求 i 的时候,使用 i-1 转移,但是现在是从 i 直接向 i+1 进行转移。
关于计算我们可以把最短路修改一部分,得到下面的式子
if(nw.x+tr[i].val<dis[num][y]) { dis[num][y]=nw.x+tr[i].val; q[num].push((node){dis[num][y],y}); } if(num!=k&&nw.x<dis[num+1][y]) { dis[num+1][y]=nw.x; q[num+1].push((node){dis[num+1][y],y}); }
这样的话,我们通过k次最短路的计算最后输出dp[ k ][ n ]就ok。
菜题练手
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn=50009; int n,m,k; int head[maxn],ecnt; struct ss{ int to,nxt,val; }tr[maxn<<1]; inline void add(int a,int b,int c) { tr[++ecnt].nxt=head[a]; tr[ecnt].to=b; tr[ecnt].val=c; head[a]=ecnt; return; } int dis[25][maxn];//经过k次改造之后到i的最短路 struct node{ int x,id; bool operator < (const node &a) const{ return x>a.x; } }; priority_queue<node> q[25]; bool vis[25][maxn]; void dij(int num) { while(!q[num].empty()) { node nw=q[num].top(); q[num].pop(); int x=nw.id; if(vis[num][x]) continue; vis[num][x]=1; for(int i=head[x];i;i=tr[i].nxt) { int y=tr[i].to; if(vis[num][y]) continue; if(nw.x+tr[i].val<dis[num][y]) { dis[num][y]=nw.x+tr[i].val; q[num].push((node){dis[num][y],y}); } if(num!=k&&nw.x<dis[num+1][y]) { dis[num+1][y]=nw.x; q[num+1].push((node){dis[num+1][y],y}); } } } return; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } for(int i=0;i<=k;i++) for(int j=1;j<=n;j++) dis[i][j]=1e9; dis[0][1]=0; q[0].push((node){0,1}); for(int i=0;i<=k;i++) dij(i); cout<<dis[min(m,k)][n]<<endl; return 0; }