BZOJ 1579 道路建设 入门分层图

那么,,先说什么是分层图呢?
在我们日常的图论中,我们一般默认 图 为二维图,即只有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 有这个思想,但是数据范围不适合用分层图做,可以去感悟一下;(笔者当然是写跪了。。。。 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值