常用技巧:分层图
一般见于图论搜索的题目中。题目中提供了一些操作,每个点可以选择进行操作。由于操作次数的不同,会产生非常多的情况,如果考虑何时使用操作,情况会更多。如果将在图上求解最短路看成是在二维平面上进行的,引入进行操作的次数 k 做为第三维,那么这个三维空间就理应可以包含所有的情况,便可以在这个三维空间上解决问题。
每一层图与原图是一样的。对于可选择的操作,在每个点上新添单向边,代表进行操作,可以转移到新图也就是下一层图当中。k个操作产生k+1层图,第 i 层图代表进行了 i 次操作后的图。
每相邻两层图之间的联系,应该展现状态的转移,体现出操作是发生在哪,结果又是到了哪。根据操作的特点,对新的有向边的边权进行修改
分层图构造完毕后,也就相当于普通的无向图/有向图。在这之上求最短路或者遍历即可。参考例题:洛谷P4568,P2939,P1073。下面是P4568代码
#include<bits/stdc++.h> using namespace std; int n,m,k,s,t,dis[200005];; struct edge { int next,cost; }; vector<edge> edges[200005]; struct node { int loc,dis; bool operator < (const node &x) const { return x.dis<dis; } }; priority_queue<node> q; bool mark[200005]; void dij() { memset(dis,0x3f3f3f3f,sizeof(dis)); q.push((node){s,0}); dis[s]=0; while(!q.empty()){ node now=q.top(); q.pop(); if(mark[now.loc]) continue; mark[now.loc]=true; for(int i=0;i<edges[now.loc].size();i++){ int next=edges[now.loc][i].next; if(mark[next]) continue; int cost=edges[now.loc][i].cost; if(dis[next]>now.dis+cost){ dis[next]=now.dis+cost; q.push((node){next,dis[next]}); } } } } int main() { scanf("%d%d%d",&n,&m,&k); scanf("%d%d",&s,&t); for(int i=1;i<=m;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); for(int j=0;j<=k;j++){ edges[a+j*n].push_back((edge){b+j*n,c}); edges[b+j*n].push_back((edge){a+j*n,c}); if(j!=k){ edges[a+j*n].push_back((edge){b+j*n+n,0}); edges[b+j*n].push_back((edge){a+j*n+n,0}); } } } for(int i=0;i<k;i++){ edges[t+i*n].push_back((edge){t+i*n+n,0}); } t=t+k*n; n=(k+1)*n; dij(); printf("%d\n",dis[t]); return 0; }