bzoj 4681

3 篇文章 0 订阅
2 篇文章 0 订阅

题目链接

题意

有一张边有权的无向图,现在要从1号点前往n号点,然后可以交换k次边权,问最短路最短是多少

数据范围

n ≤ 50 , m ≤ 150 , k ≤ 20 n\le 50,m\le 150,k\le 20 n50,m150,k20

解法

最短路:考虑现在在一个点上,需要多计算的状态有什么:首先我们需要知道走到这个点上交换了几次边权,然后还有现在边的状态.

第一个很好记录,但是第二个并不好办.所以考虑加上一些限制,使得我们可以记录第二个状态:将边按边权从小到大排序后,我们枚举交换的边最多使用前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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值