4681: [Jsoi2010]旅行
Time Limit: 30 Sec
Memory Limit: 256 MB
Description
WJJ喜欢旅游,这次她打算去一个据说有很多漂亮瀑布的山谷玩。
WJJ事先得到了一张地图,上面标注了N(1< = N< = 50)个小动物的聚居地,也就是一个个的小村落。其中第1个村庄是WJJ现在住的地方,第N个村庄是WJJ打算去的地方。这些村庄之间有M(1< = M< = 150)条双向道路连接着,第j条双向道路恰好直接连接两个小村庄A,B,长度为C(1< = A,B < = N,Ai<>B, 1< = C< = 1000)。道路有的是隧道,有的是栈桥,地图上那些看起来在村庄之外交叉的路实际上并不相交——也就是说,如果把这些小村落和双向道路构成的道路网看作图论意义上的图,我们不保证它是平面图,也不保证它没有重边。不过,有一点还是可以保证的:WJJ细心地验证过,从它居住的村落一定能走到她想去的那个山谷。在WJJ所在的神奇世界中,每只小动物都可以借助仙人掌来施放魔法,其中之一是,交换世界中任意两条双向道路的长度,同时保持其他道路的长度不变。按WJJ目前的魔法水平,她最多能使用K(1< = K< = 20)次这种道路长度交换魔法。可惜的是,由于仙人掌刺比较多,WJJ并不打算带着它旅行,于是她会在家里完成想要的道路交换后再出门。假设WJJ的旅行途中不会有其他小动物进行道路交换来破坏她设计好的路线。为了尽快达到目的地,WJJ希望她需要走的总距离越短越好。也就是说,使用最多K次魔法后,从村落1到村落N的最短距离是多少?
Input
第1行:3个用空格隔开的整数N,M,K
第2行:M+1行:每行是3个整数,用空格隔开,分别表示A,B, C
Output
1个整数,表示使用最多K次魔法后,村落1和村落N之间的最短距离。
Sample Input
5 5 2
1 2 10
2 5 10
1 3 4
3 4 2
4 5 1
Sample Output
3
一个可行的方案是,对调第1条边和第4条边的长度,再对调第1条边和第5条边的长度。对调后的最短路径为1–>2–>5,长度为3。显然没有比这更优的方案了。
解:
震惊,这最短路简直神了。本来想的最短路树,但是可以交换的边特别多,似乎不是很支持。hzwer的想法也很好,就是无视条边的最短路。
简单来说,这么少的点,如果要跑图论算法那么只能想到拆点。从hzwer的想法出发。我们发现一个性质,就是我们最后的答案路线一定会有前
L
L
小的路线。我们枚举,无视前
L
L
小的边跑最短路。这样的话加上交换次数,我们就要把点拆成三维的。用表示到第
x
x
号点路径上有条前
L
L
小的边,交换了次的最短路。而且这样做的好处是我们默认前
L
L
条边都在最短路上,所以我们并不需要考虑这条边出现的顺序。
于是我们有了这几个转移:
如果下一条边在前
L
L
条边,
dis[eg[i].to][y+1][z]<——dis[x][y][z]+data[y+1].len
如果下一条边不在前条里:
我们有两种选择(交换或者不交换):
dis[eg[i].to][y+1][z+1]<——dis[x][y][z]+data[y+1].len
dis[eg[i].to][y][z]<——dis[x][y][z]+data[(i+1)/2].len
我们只要枚举 L L 就好了。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct lxy{
int to,next;
}eg[305];
struct lxy1{
int op,ed,len;
bool operator < (const lxy1 &QAQ)const{
return len<QAQ.len;
}
}data[155];
struct colder{
int x,y,z,dis;
bool operator < (const colder &QAQ)const{
return dis>QAQ.dis;
}
}yyy;
priority_queue <colder> d;
int n,m,head[55],k,cnt,ans=0x7f7f7f7f;
int dis[55][155][155];
bool vis[55][155][155];
void add(int op,int ed){
eg[++cnt].to=ed;
eg[cnt].next=head[op];
head[op]=cnt;
}
void coldcold(int l)
{
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[1][0][0]=0;yyy.x=1;yyy.y=0;yyy.z=0;yyy.dis=0;d.push(yyy);
while(!d.empty()){
int x=d.top().x,y=d.top().y,z=d.top().z;
vis[x][y][z]=1;d.pop();
for(int i=head[x];i!=-1;i=eg[i].next)
{
if(i<=l*2){
if(y<l&&dis[eg[i].to][y+1][z]>dis[x][y][z]+data[y+1].len)
dis[eg[i].to][y+1][z]=dis[x][y][z]+data[y+1].len,yyy.x=eg[i].to,yyy.y=y+1,yyy.z=z,yyy.dis=dis[eg[i].to][y+1][z],d.push(yyy);
}
else{
if(y<l&&z<k&&dis[eg[i].to][y+1][z+1]>dis[x][y][z]+data[y+1].len)
dis[eg[i].to][y+1][z+1]=dis[x][y][z]+data[y+1].len,yyy.x=eg[i].to,yyy.y=y+1,yyy.z=z+1,yyy.dis=dis[eg[i].to][y+1][z+1],d.push(yyy);
if(dis[eg[i].to][y][z]>dis[x][y][z]+data[(i+1)/2].len)
dis[eg[i].to][y][z]=dis[x][y][z]+data[(i+1)/2].len,yyy.x=eg[i].to,yyy.y=y,yyy.z=z,yyy.dis=dis[eg[i].to][y][z],d.push(yyy);
}
}
while(!d.empty()&&vis[d.top().x][d.top().y][d.top().z]==1) d.pop();
}
for(int i=0;i<=k;i++) ans=min(ans,dis[n][l][i]);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&data[i].op,&data[i].ed,&data[i].len);
sort(data+1,data+1+m);
for(int i=1;i<=m;i++){
add(data[i].op,data[i].ed);
add(data[i].ed,data[i].op);
}
for(int i=0;i<=m;i++) coldcold(i);
printf("%d",ans);
}
本文介绍了一种解决特定旅行问题的算法,通过拆点的方法找到使用最多K次道路长度交换魔法后,从起点到终点的最短路径。该算法利用了最短路径树的概念,并通过枚举关键边的数量优化解决方案。
408

被折叠的 条评论
为什么被折叠?



