题意
给定一个无向图,可以选择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]); }