將邊按權值從小到大排序。
考慮一條路徑,一定是最大的若干條邊和最小的相應的沒選的邊進行交換。
這會導致存在一個分界線$L$,交換之後恰好選中前$L$小的邊,且只允許$>L$的邊與$\leq L$的邊進行交換。
列舉$L$,設$f[i][j][k]$表示從$1$到$i$,經過了$j$條前$L$小的邊,捨棄了$k$條$>L$的邊時,$>L$且未捨棄的邊權和的最小值。
用Dijkstra演算法求出$f$,更新答案即可。
時間複雜度$O((n+m)m^2k)$。
#include<cstdio> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef pair<int,int>P; const int N=55,M=155,inf=~0U>>1; int n,m,K,i,j,k,L,g[N],v[M<<1],nxt[M<<1],ed,base,ans=inf,f[N][M][22]; priority_queue<P,vector<P>,greater<P> >q; struct E{int x,y,w;}e[M]; inline bool cmp(const E&a,const E&b){return a.w<b.w;} inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void ext(int x,int y,int z,int w){ if(y>L||z>K)return; if(f[x][y][z]<=w)return; q.push(P(f[x][y][z]=w,(x<<13)|(y<<5)|z)); } int main(){ scanf("%d%d%d",&n,&m,&K); for(i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w); sort(e+1,e+m+1,cmp); for(ed=i=1;i<=m;i++)add(e[i].x,e[i].y),add(e[i].y,e[i].x); for(L=0;L<=m;L++){ base+=e[L].w; if(base>=ans)break; for(i=1;i<=n;i++)for(j=0;j<=L;j++)for(k=0;k<=K;k++)f[i][j][k]=inf; ext(1,0,0,base); while(!q.empty()){ P t=q.top();q.pop(); int z=t.second&31;t.second>>=5; int y=t.second&255;t.second>>=8; int x=t.second; if(f[x][y][z]<t.first)continue; for(i=g[x];i;i=nxt[i])if((i>>1)<=L)ext(v[i],y+1,z,t.first); else{ ext(v[i],y,z,t.first+e[i>>1].w); ext(v[i],y,z+1,t.first); } } for(j=0;j<=L;j++)for(k=0;k<=K;k++)if(j+k<=L&&f[n][j][k]<ans)ans=f[n][j][k]; } return printf("%d",ans),0; }