题意
有一张边有权的无向图,现在要从1号点前往n号点,然后可以交换k次边权,问最短路最短是多少
数据范围
n ≤ 50 , m ≤ 150 , k ≤ 20 n\le 50,m\le 150,k\le 20 n≤50,m≤150,k≤20
解法
最短路:考虑现在在一个点上,需要多计算的状态有什么:首先我们需要知道走到这个点上交换了几次边权,然后还有现在边的状态.
第一个很好记录,但是第二个并不好办.所以考虑加上一些限制,使得我们可以记录第二个状态:将边按边权从小到大排序后,我们枚举交换的边最多使用前l条.并且钦定这l条边的使用顺序是从小到大.
这样就可以设计出转移状态:dis[u][j][w]表示当前走到u号点,前l条边已经使用了j条,其中交换来的有w条.
转移分3种情况讨论
1 当前要走的这条边就是前l条边中的一条,那么由于我们要求的使用顺序的限制,我们会把第j+1条边和当前这条边换掉,但是由于这条边也是一定要用的,所以不消耗交换次数
if(id<=l){
if(j<l&&dis[v][j+1][w]>dis[u][j][w]+a[j+1].c){
dis[v][j+1][w]=dis[u][j][w]+a[j+1].c;
if(!vis[v][j+1][w]){
q.push(node(v,j+1,w));
vis[v][j+1][w]=1;
}
}
}
2 当前要走的这条边不是前l条边中的一条,然后我们把它换成前l条边中的一条
if(j<l&&w<k&&dis[v][j+1][w+1]>dis[u][j][w]+a[j+1].c){
dis[v][j+1][w+1]=dis[u][j][w]+a[j+1].c;
if(!vis[v][j+1][w+1]){
q.push(node(v,j+1,w+1));
vis[v][j+1][w+1]=1;
}
}
3 我们直接走这条边,不交换
if(dis[v][j][w]>dis[u][j][w]+e[i].w){
dis[v][j][w]=dis[u][j][w]+e[i].w;
if(!vis[v][j][w]){
q.push(node(v,j,w));
vis[v][j][w]=1;
}
}
最后统计答案的时候,需要枚举交换的次数(这里不一定交换的越多就越优,因为dis数组的含义是恰好交换了w次时的最短路)
for(int i=0;i<=k;i++)ans=min(ans,dis[n][l][i]);
全部代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=155;
inline int read(){
char c=getchar();int t=0,f=1;
while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,m,k;
struct node{
int a,b,c;
node(int aa=0,int bb=0,int cc=0){a=aa,b=bb,c=cc;}
}a[maxn];
struct edge{
int v,p,w,id;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b,int c){
e[++cnt].p=h[a];
e[cnt].v=b;
e[cnt].w=c;
h[a]=cnt;
e[++cnt].p=h[b];
e[cnt].v=a;
e[cnt].w=c;
h[b]=cnt;
}
bool cmp(node a,node b){
return a.c<b.c;
}
int ans=0x3f3f3f3f;
int dis[maxn][maxn][maxn];//第i个点,前l条边用了j条,路径上有k条边本来大于L,但是被换了
typedef pair<int,int> pii;
queue<node> q;
int vis[maxn][maxn][maxn];
void dj(int l){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[1][0][0]=0;
q.push(node(1,0,0));
while(!q.empty()){
node now=q.front();int u=now.a,j=now.b,w=now.c;q.pop();
vis[u][j][w]=0;
for(int i=h[u];i;i=e[i].p){
int v=e[i].v,id=(i+1)/2;
if(id<=l){
if(j<l&&dis[v][j+1][w]>dis[u][j][w]+a[j+1].c){
dis[v][j+1][w]=dis[u][j][w]+a[j+1].c;
if(!vis[v][j+1][w]){
q.push(node(v,j+1,w));
vis[v][j+1][w]=1;
}
}
}
else{
if(j<l&&w<k&&dis[v][j+1][w+1]>dis[u][j][w]+a[j+1].c){
dis[v][j+1][w+1]=dis[u][j][w]+a[j+1].c;
if(!vis[v][j+1][w+1]){
q.push(node(v,j+1,w+1));
vis[v][j+1][w+1]=1;
}
}
if(dis[v][j][w]>dis[u][j][w]+e[i].w){
dis[v][j][w]=dis[u][j][w]+e[i].w;
if(!vis[v][j][w]){
q.push(node(v,j,w));
vis[v][j][w]=1;
}
}
}
}
}
for(int i=0;i<=k;i++)ans=min(ans,dis[n][l][i]);
}
int main(){
// freopen("2.in","r",stdin);
// freopen("2b.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
a[i].a=read(),a[i].b=read(),a[i].c=read();
}
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++){
add(a[i].a,a[i].b,a[i].c);
}
for(int l=0;l<=m;l++){
dj(l);//其实是spfa
// printf("%d\n",ans);
}
printf("%d\n",ans);
return 0;
}