[JLOI2011]飞行路线 (暑假D3 拆点+dijkstra堆优化)

题意

给定一个无向图,可以选择k条边使其权值为0,求从s到t的路径最小权值和。

对于100%的数据,2<=n<=1e4,1<=m<=5e4,0<=k<=10,权值<=1e3

题解

有一种神奇的做法就是拆点,对于一个点拆成k+1个点,代表用了k次操作;

对于k相同的点分为一层,每层层内的连边相同,每一层有点向下一层连边,代表从这个点用一次操作到下个点,前提是与下个点在这层对应点有连边。

有可能用不了k次操作,但为了方便,我们将每层的终点用0的边连接,最后最需要查最后一层的终点的值即可。

#include<bits/stdc++.h>
using namespace std;

const int maxn=110005;
int n,m,k,s,t;
vector<pair<int,int> >e[maxn];

void add(int x,int y,int z){e[x].push_back(make_pair(y,z));}

int dis[maxn];
bool vis[maxn];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;

void dijkstra(){
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push(make_pair(dis[s],s));
    while(!q.empty()){
      int x=q.top().second;
      q.pop();
      if(vis[x]) continue;
      vis[x]=true;
      for(unsigned int i=0;i<e[x].size();i++){
        int y=e[x][i].first;
        if(dis[y]>dis[x]+e[x][i].second){
          dis[y]=dis[x]+e[x][i].second;
          q.push(make_pair(dis[y],y));
        }
      }
    }
    
}

int main(){
    scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
    s++;t++;
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        x++;y++;
        add(x,y,z);add(y,x,z);
        for(int j=1;j<=k;j++){
            add(x+j*n,y+j*n,z);
            add(y+j*n,x+j*n,z);
            add(x+(j-1)*n,y+j*n,0);
            add(y+(j-1)*n,x+j*n,0);
        }
    }
    for(int i=1;i<=k;i++)
     add(t+(i-1)*n,t+i*n,0);
    dijkstra();
    printf("%d",dis[t+n*k]);
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11228344.html