那么,,先说什么是分层图呢?
在我们日常的图论中,我们一般默认 图 为二维图,即只有x,y;但是发现在某些情况下无法解决一些问题,比如涉及到动态问题
这时候单纯的图是没有办法满足的,当然你也可以枚举所有情况,不过意义何在呢?
所以这时候既然原有的二维图没法搞,那就需要一种新的建图思想了;
------------------------------分割线------------------------------------------------
分层图是一种基于dp+枚举+图论得到的新的思想;
具体来说就是,将题中所给的不确定状态放在新复制出来的原图上,新图与原图有约定的方式相互转移,这样通过n(所需改变状态的数量的排列组合)层图,from 1 -> n进行一遍BFS(或者说,,可以套 Astar) or spfa/dijstrka 从而在目标状态层下得到最优解;
不过你可能要问,每个状态都建一层图,那 时间空间不就双双爆炸了吗?
现在考虑一下我们刚刚提到的,dp+枚举+图论这个想法,枚举和图论已经搞定了,那dp呢?很自然的想到可以开多维数组来表示状态与层级,而且还避免了手动枚举的漏解的可能;
那么,举一个很简单的例子:BZOJ 1579 一道状态变动仅为1的分层图,题中要求,可以有k条边变为0(层与层之间的转移)
求1到n的最短路;
可以发现啊这里的 k 条边就十分符合我们说的动态的标准,那先考虑k==1的情况,我们可以枚举,每条边为0的情况下,spfa求最短路;进一步去想,k==2,枚举边的排列组合,方案数为 (1+n)*n/2, k==3,,,不枚举了哈,总之我们发现,随着k的增大,枚举次数几乎是爆炸式增长;不过这还是给了一个很好的想法;排列组合的时候发现,k==n,是基于k==n-1来的,所以不妨在 k==n上,向下拓展,即递推一层,即可解决爆炸增长的次数,因为这时候只需要一遍dijstrka(),每的到下一层,就入队,然后尝试更新,最终在 dis[n][k] 得解;
------在此%%% Siriusren 学长------
在我们日常的图论中,我们一般默认 图 为二维图,即只有x,y;但是发现在某些情况下无法解决一些问题,比如涉及到动态问题
这时候单纯的图是没有办法满足的,当然你也可以枚举所有情况,不过意义何在呢?
所以这时候既然原有的二维图没法搞,那就需要一种新的建图思想了;
------------------------------分割线------------------------------------------------
分层图是一种基于dp+枚举+图论得到的新的思想;
具体来说就是,将题中所给的不确定状态放在新复制出来的原图上,新图与原图有约定的方式相互转移,这样通过n(所需改变状态的数量的排列组合)层图,from 1 -> n进行一遍BFS(或者说,,可以套 Astar) or spfa/dijstrka 从而在目标状态层下得到最优解;
不过你可能要问,每个状态都建一层图,那 时间空间不就双双爆炸了吗?
现在考虑一下我们刚刚提到的,dp+枚举+图论这个想法,枚举和图论已经搞定了,那dp呢?很自然的想到可以开多维数组来表示状态与层级,而且还避免了手动枚举的漏解的可能;
那么,举一个很简单的例子:BZOJ 1579 一道状态变动仅为1的分层图,题中要求,可以有k条边变为0(层与层之间的转移)
求1到n的最短路;
可以发现啊这里的 k 条边就十分符合我们说的动态的标准,那先考虑k==1的情况,我们可以枚举,每条边为0的情况下,spfa求最短路;进一步去想,k==2,枚举边的排列组合,方案数为 (1+n)*n/2, k==3,,,不枚举了哈,总之我们发现,随着k的增大,枚举次数几乎是爆炸式增长;不过这还是给了一个很好的想法;排列组合的时候发现,k==n,是基于k==n-1来的,所以不妨在 k==n上,向下拓展,即递推一层,即可解决爆炸增长的次数,因为这时候只需要一遍dijstrka(),每的到下一层,就入队,然后尝试更新,最终在 dis[n][k] 得解;
------在此%%% Siriusren 学长------
#include <queue>
#include <cstdio>
#include <cstrin>
#include <algorithm>
using namespace std;
#define N 100050
int n,m,k,head[N],ver[N],w[N],nxt[N],tot,dis[N][21],vis[N][21],xx,yy,zz;
void add(int x,int y,int z){
w[tot]=z,ver[tot]=y,nxt[tot]=head[x],head[x]=tot++;
}
struct Node{int now,level,weight;}t;
bool operator < (Node a,Node b){return a.weight>b.weight;}
void Dijstrka(){
memset(dis,0x3f,sizeof(dis)),dis[1][0]=0;
priority_queue<Node>q;//小根堆优化dij
t.now=1,q.push(t);
while(!q.empty()){
Node now=q.top();q.pop();
if(vis[now.now][now.level])continue;
vis[now.now][now.level]=1;
for(int i=head[now.now];~i;i=nxt[i]){
if(!vis[ver[i]][now.level]&&dis[ver[i]][now.level]>dis[now.now][now.level]+w[i]){
dis[ver[i]][now.level]=dis[now.now][now.level]+w[i];
t.level=now.level,t.now=ver[i],t.weight=dis[ver[i]][now.level],q.push(t);
}
if(now.level<k&&!vis[ver[i]][now.level+1]&&dis[ver[i]][now.level+1]>dis[now.now][now.level]){//向下一层更新,并入队作为可///尝试节点更新
dis[ver[i]][now.level+1]=dis[now.now][now.level];
t.level=now.level+1,t.now=ver[i],t.weight=dis[ver[i]][now.level+1],q.push(t);
}
}
}
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&xx,&yy,&zz);
add(xx,yy,zz),add(yy,xx,zz);
}
Dijstrka();
printf("%d\n",dis[n][k]);
}
其实分层图还可以解决很多走地图/图 上得问题,很多限制条件都可以作为一个新维度,通过判断,剪枝,优化,可以解决一些看似dp又不是dp得问题;笔者学的时候有看到过,可惜没能深入学习,有兴趣的读者可以去看看,今年HEOI day2t2 有这个思想,但是数据范围不适合用分层图做,可以去感悟一下;(笔者当然是写跪了。。。。