这个题如果我们先想用平常的方法来建图,因为我们无法确定是否使用卡片,如果我们每个点每个边都建图,那么非常耗时占空间;注意到k是比较小的,所以我们可以把k拆开,把一个点分为k个,分别表示用k张卡片所走的最短路,我们可以理解为走了k个图,相邻图之间的路变为原来所走的路的一半,所以这样建图:各层内部正常连边,各层之间权值为一半的边。每跑一层,就相当于使用一次卡片。跑一遍从s到t+n*k的最短路即可,第i层和第i+1层之间路权值变为原来的一半;相当于用了一次卡,这里我用了dijkstra的堆优化(多练习一下刚学会),spfa也可以过;
#include<algorithm> #include<bitset> #include<cctype> #include<cerrno> #include<clocale> #include<cmath> #include<complex> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<deque> #include<exception> #include<fstream> #include<functional> #include<limits> #include<list> #include<map> #include<iomanip> #include<ios> #include<iosfwd> #include<iostream> #include<istream> #include<ostream> #include<queue> #include<set> #include<sstream> #include<stack> #include<stdexcept> #include<streambuf> #include<string> #include<utility> #include<vector> #include<cwchar> #include<cwctype> using namespace std; int h,n,m,k,s,t,a,b,c,tot,lin[220009]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } struct edge { int y,v,next; }an[4200009]; int dis[220009]; bool vis[220009]; typedef pair <int,int> pii; priority_queue <pii,vector<pii>,greater<pii> > q; void add(int x,int y,int z) { an[++tot].y=y; an[tot].v=z; an[tot].next=lin[x]; lin[x]=tot; } void diskstra_heap(int s) { memset(dis,127,sizeof(dis)); dis[s]=0; q.push(make_pair(dis[s],s)); int x,j; while (q.size()) { x=q.top().second; q.pop(); if(vis[x]) continue; vis[x]=true; for (int i=lin[x];i;i=an[i].next) { j=an[i].y; if(dis[x]+an[i].v>=dis[j]) continue; dis[j]=dis[x]+an[i].v; q.push(make_pair(dis[j],j)); } } } int main() { n=read();m=read();k=read(); for (int i=1;i<=m;++i) { a=read();b=read();c=read(); add(a,b,c); add(b,a,c); for (int j=1;j<=k;++j) { add(j*n+a,j*n+b,c); add(j*n+b,j*n+a,c); add((j-1)*n+a,j*n+b,c/2); add((j-1)*n+b,j*n+a,c/2); } } s=1,t=n;//从1到n的路径 diskstra_heap(s); int ans=dis[t]; for(int i=0;i<=k;++i) ans=min(ans,dis[i*n+t]); cout<<ans; return 0; }