/* 返回顶部 */

Luogu P4568 [JLOI2011]飞行路线

传送

gg给的题解

这道题的k很小,把k看作一种状态,可以考虑用分层的方法解决。

把每个点分成k个,dis[x][k] 代表使用了k次免费机会,到达x的最短距离。

从u到v,可以不使用免费机会,费用为val,或者使用免费机会,边权为0。

那么连边的时候,每条边实际要连2*(k+1)条:(i = 0~k) 从[x][i]到[y][i],边权为val;从[x][i]到[y][i+1],边权为0。

然后直接跑最短路就可以了。

实际写的时候不需要开二维数组,因为一共有(n+1)*(k+1)个点(n,k都从0开始),

所以把点的编号记作x+i*n即可。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
#include<queue>
using namespace std;
const int maxn = 1e7+10;
const int INF = 0x3f3f3f3f;
int n,m,k,s,t,a,b,c;
int head[maxn],to[maxn],nxt[maxn],val[maxn];
int dis[maxn],cnt,ans;
bool vis[maxn];

void add(int x,int y,int z) {
    to[++cnt] = y;
    nxt[cnt] = head[x];
    head[x] = cnt;
    val[cnt] = z;
}

void init() {
    for(int i = 0; i <= n; i++)
        for(int j = 0; j <= k; j++)
            dis[i+j*n] = INF;
}


void dijkstra(int s) {
    init();
    priority_queue < pair < int,int > , vector < pair < int,int > >,greater < pair < int,int > > > q;
    dis[s] = 0;
    q.push(make_pair(0,s));
    while(!q.empty()) {
        int u = q.top().second;
        q.pop();
        if(vis[u])continue;
        vis[u] = true;
        for(int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            if(dis[u] + val[i] < dis[v]) {
                dis[v] = dis[u] + val[i];
                q.push(make_pair(dis[v],v));
            }
        }
    }
}

int main() {
    scanf("%d%d%d",&n,&m,&k);
    scanf("%d%d",&s,&t);
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
        for(int j = 1; j <= k; j++) {
            add(a+j*n,b+j*n,c);
            add(b+j*n,a+j*n,c);
            add(a+(j-1)*n,b+j*n,0);
            add(b+(j-1)*n,a+j*n,0);
        }
    }
    dijkstra(s);
    ans = INF;
    for(int i = 0; i <= k; i++)
        ans = min(ans,dis[t+i*n]);
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2019-05-21 20:34  Mogeko  阅读(169)  评论(0编辑  收藏  举报