洛谷 P4568 [JLOI2011]飞行路线

洛谷

套路题,分层图(然鹅我并不会,是看了题解才打出来的)。

因为k值很小,所以我们可以建k+1张图。

对于一条边e,起点为u,终点为v,我们可以把上一层图的u与这一层图的v相连,边权为0,表示使用一次免费的机会。而这一层图的u也向v连边,边权为花费。

因为只有k+1个图,所以图与图之间只有k个间隔,故只能使用k次免费的机会,所以我们在这种分层图中跑最短路就可以得到答案\(ans=dis[ed+k*n]\)

补充:这一题spfa会被卡,要跑堆优化dijkstra。

哦,还有值得注意的一点,我们可以将每一层的终点向下一层的点连一条0边,可以防止奇葩数据——并不要用k次机会就已经到了终点,所以之后的点不被松弛,故\(dis[ed+k*n]!=ans\)

代码在这儿~

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pr;

const int N=110005;
bool vis[N];
int n,m,k,st,ed,s[N<<6][3],o[N],cnt,dis[N];
priority_queue <pr,vector<pr >,greater<pr > > q;

void read(int &aa)
{
    aa=0;char c=getchar();
    while (c>'9'||c<'0') c=getchar();
    while (c>='0'&&c<='9')
        aa=(aa<<3)+(aa<<1)+(c^48),c=getchar();
}

void add(int x,int y,int c)
{
    s[++cnt][0]=y,s[cnt][1]=o[x],s[cnt][2]=c,o[x]=cnt;
}

void dijkstra()
{
    int nn=n*k+n;
    for (int i=1;i<=nn;++i) dis[i]=1e9;
    dis[st]=0;
    q.push(make_pair(0,st));
    while (!q.empty()) {
        int x=q.top().second;
        q.pop();
        if (vis[x]) continue;
        vis[x]=1;
        for (int i=o[x];i;i=s[i][1]) {
            int y=s[i][0],c=s[i][2];
            if (dis[y]>dis[x]+c) {
                dis[y]=dis[x]+c;
                q.push(make_pair(dis[y],y));
            }
        }
    }
}

int main()
{
    int x,y,c;
    read(n),read(m),read(k),read(st),read(ed);
    ++st,++ed;
    for (int i=1;i<=m;++i) {
        read(x),read(y),read(c);
        ++x,++y;
        for (int j=1;j<=k;++j) {
            add(x+(j-1)*n,y+j*n,0);
            add(y+(j-1)*n,x+j*n,0);
            add(x+j*n,y+j*n,c);
            add(y+j*n,x+j*n,c);
        }
        add(x,y,c),add(y,x,c);
    }
    for (int i=1;i<=k;++i) add(ed+(i-1)*n,ed+i*n,0);
    dijkstra();printf("%d",dis[ed+k*n]);
    return 0;
}
posted @ 2018-08-22 11:34  fuyan0101  阅读(260)  评论(0编辑  收藏  举报