• ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层dijkstra)


    题意:有n个地方,m条带权值的路,你有k次机会将一条路的权值变为0,求1到n的最短距离。

    分析:这是一题分层dijkstra的模板题,因为k的大小不是很大,最坏的情况也就是10,而我们一般的最短路问题只是建一个平面的图。

    而分层dijkstra是一个空间的。怎么操作呢?

    举个简单的栗子

    当数据如下是

    n  m  k

    3  2  1

    1  2  5(路的起点1,终点2,权值5)

    2  3  6

    没有k的情况就是这样的

    如果有k的情况,即1到2权值可能为0,2到3也可能为0,我们可以加一个平面表示他们的关系

    将4,5,6(地方)表示1,2,3的关系图形如下:(图很丑,将就着看。。。)

    什么这样表示呢?   因为我们可能理解1到2可能变成0,所以1到5的距离为0,2到3可能变为0即2到6距离为0.

    答案就是1到3的最短路与1到6的最短路最小值。

    我们可以通过这样一个简单的例子来理解:

    1.k的大小关系到分的层数(k+1层)。

    2.只有层与层之间的路才为0,这样使用的k值就不会超过给定的。

    3.而答案可能没用到k,那答案就是1到n的最短路,如果用一次就是1到2*n的最短路,如果用k次就是1到(k+1)*n的最短路

    所以答案就是min(d[n],d[n*2]....d[(k+1)*n])  (d[]表示1到其的距离)。

    这个方法很巧妙地避免了使用k的问题。只要先建好图,直接用dijkstra.

     这里还要注意几个问题:

    1.图的存储方式,m的范围有点大,不能用邻接矩阵,这里用的是链式前向星,邻接表也可以。

    2.数组的大小,因为图最坏是11层(最坏11*m),层与层之间也有路(最坏10*m),所以最少要开21*m的数组

    3.这里用了优先队列,防止时间超时。(不知道不用会不会超时,没试过)。

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const ll maxn=3e6+10;
    struct node{
        ll from,to;
        ll next;
        ll w;//权值
    }edge[2*maxn];//前向星 
    struct point{
        ll y,val;
    }t,p;
    ll head[maxn],visit[maxn],d[maxn];
    ll ans,cnt,k,n,m;
    bool operator < (point a,point b)//优先路径短的 
    {
        if(a.val==b.val)
            return a.y<b.y;
        return a.val>b.val;
    }
    priority_queue<point> q;
    
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(visit,0,sizeof(visit));
        memset(d,0x3f,sizeof(d));
        cnt=0;
    }
    
    void add(ll u,ll v,ll w)//前向星加边 
    {
        edge[cnt].from=u;
        edge[cnt].to=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    
    void dijkstra(int u,int v)//dijkstra 
    {
        d[u]=0;
        t.y=u,t.val=0;
        q.push(t);
        while(!q.empty())
        {
            p=q.top();
            q.pop();
            if(visit[p.y])
                continue;
            visit[p.y]=1;
            for(int i=head[p.y];i!=-1;i=edge[i].next)
            {
                int e=edge[i].to;
                //cout<<e<<endl;
                if(d[e]>d[p.y]+edge[i].w)
                {
                    d[e]=d[p.y]+edge[i].w;
                    t.y=e,t.val=d[e];
                    q.push(t);    
                }    
            }        
        }
        ans=inf;
        for(int i=0;i<=k;i++)//取d[n],d[n*2]...d[(k+1)*n]最小 
        {
            if(ans>d[v+i*n])
                ans=d[v+i*n];
        }
        cout<<ans<<endl;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            init();
            scanf("%lld%lld%lld",&n,&m,&k);
            if(k>=m)//k大于边数,无疑答案是0 
            {
                cout<<0<<endl;
                continue;
            }
            for(int i=1;i<=m;i++)
            {
                ll u,v,w;
                scanf("%lld%lld%lld",&u,&v,&w);
                for(int j=0;j<=k;j++)
                {
                    add(u+j*n,v+j*n,w);//每一层地方的关系,建图 
                    if(j!=k)
                        add(u+j*n,v+(j+1)*n,0);//层与层之间的关系,建图 
                }    
            }
            dijkstra(1,n);
        }
        return 0;
    }
  • 相关阅读:
    luogu P2439 [SDOI2005]阶梯教室设备利用
    bzoj1559: [JSOI2009]密码
    bzoj3172: [Tjoi2013]单词
    后缀树简短实现
    [APIO2010]特别行动队 --- 斜率优化DP
    [APIO2014]序列分割 --- 斜率优化DP
    [HNOI2012]集合选数 --- 状压DP
    UVA11107 Life Forms --- 后缀数组
    [TJOI2017]DNA --- 后缀数组
    [NOI2014]购票 --- 斜率优化 + 树形DP + 数据结构
  • 原文地址:https://www.cnblogs.com/xiongtao/p/9642152.html
Copyright © 2020-2023  润新知