两种K短路问题
2014-02-23 16:31
155 查看
一:普通的K短路问题。
这个就是在一张图里面求k短路长度。。没有什么限制。
这个可以用A*算法解决。
先说大概思路:用优先队列储存状态。。。每次出队step值(就是当前走到这个点所经过的距离值)最小的点,用它扩展到周围的点的状态。。然后把这些新状态入队。。。
(就和普通bfs差不多),那么目标点第一次出队时,这个状态的step值就是他的第一短路。。同理,第k次出队时所对应状态的step值就是他的k短路。。
然后考虑加进A*。。。每个状态的估价函数g设计为从当前点到目标点的最短路(预处理一次得出:反建边,从目标点求单源最短路)。。然后把优先队列改成每次出队(step+g)值最小的状态。。
大概就是这样了。。。(如果不理解什么是A*的话可能看不懂,建议去翻一下A*相关资料或者给我留言- -)
还是要求k短路。。只不过每条路线都必须不经过重复的点。。用刚才的方法就不能保证是简单路了(或者说考虑状压?可能也行。。但我没试过)
关于这个有一个专门的偏离算法。。。不过我没学
就说说当点比较少的时候的情况,比如这道题。
基本的思想就是二分路径长度,每次搜索只找到有多少存在条长度小于二分值mid的路径(需要知道的是有没有k条或以上这样的路径,所以如果已经找到的k条那么就不要继续搜索了)。。。
(于是当路径值大于mid时就剪枝。。)这里还是可以借鉴上面一个问题的某个地方。。。算出每个点到目标点的最短路来剪枝。
二分出求出最后的mid之后。我们便知道要求的路径的长度一定为mid。
于是我们再通过一次搜索求出长度小于等于mid-1的路径有x条,那么我们要求的路径就转换为长度为mid,字典序为第k-x小的路径。
那么就直接再搜一次,每次都先走最小能到的点,然后走到终点长度为mid的边才统计。一直走到第k-x个被统计的路径。。。这个路径,就是答案,输出就完了
有个比较坑的特殊数据要这样才能过:由于一开始求出的那个最短路没有考虑有些点不能走。。。所以每搜到一个dfs节点就要重新计算没经过已走点的最短路,然后再用来剪枝。。这样判断的代价非常高,所以当前点能过了,但其他点的时间通通大幅增加,最后一个点还会T。。不过BZOJ上算的是总时间。。。所以也就水过了。(这种高代价剪枝。。考场上用风险太大了)
代码实际上用了mato神犇说的迭代变优搜索:http://www.cppblog.com/MatoNo1/archive/2012/09/23/191705.html
这个就是在一张图里面求k短路长度。。没有什么限制。
这个可以用A*算法解决。
先说大概思路:用优先队列储存状态。。。每次出队step值(就是当前走到这个点所经过的距离值)最小的点,用它扩展到周围的点的状态。。然后把这些新状态入队。。。
(就和普通bfs差不多),那么目标点第一次出队时,这个状态的step值就是他的第一短路。。同理,第k次出队时所对应状态的step值就是他的k短路。。
然后考虑加进A*。。。每个状态的估价函数g设计为从当前点到目标点的最短路(预处理一次得出:反建边,从目标点求单源最短路)。。然后把优先队列改成每次出队(step+g)值最小的状态。。
大概就是这样了。。。(如果不理解什么是A*的话可能看不懂,建议去翻一下A*相关资料或者给我留言- -)
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; int n,m; int S,T,k; int head[1000+10],l=0; int dis[1000+10]; int cnt[1000+10]; bool vis[1000+10]; struct E { int v,w,next; bool rev; }e[100000*2+10]; struct state { int step,u; bool operator <(const state &b)const { return step+dis[u]>b.step+dis[b.u]; } }start; typedef pair<int,int>pii; void add(int u,int v,int w) { e[l].v=v; e[l].w=w; e[l].next=head[u]; e[l].rev=0; head[u]=l++; e[l].v=u; e[l].w=w; e[l].next=head[v]; e[l].rev=1; head[v]=l++; } int dijkstra() { memset(dis,0x3f,sizeof(dis)); dis[T]=0; priority_queue<pii,vector<pii>,greater<pii> >q; q.push(make_pair(dis[T],T)); while(!q.empty()) { int x=q.top().second; q.pop(); if(vis[x])continue; vis[x]=true; for(int i=head[x];i!=-1;i=e[i].next) { if(e[i].rev) { int v=e[i].v; if(dis[v]>dis[x]+e[i].w) { dis[v]=dis[x]+e[i].w; q.push(make_pair(dis[v],v)); } } } } } void Astar() { priority_queue<state>q; start.u=S; start.step=0; q.push(start); while(!q.empty()) { state cs=q.top(); q.pop(); cnt[cs.u]++; if(cnt[cs.u]>k)continue; if(cs.u==T) { if(k==cnt[cs.u]) { printf("%d\n",cs.step); exit(0); } } for(int i=head[cs.u];i!=-1;i=e[i].next) { if(!e[i].rev) { state ns; ns.u=e[i].v; ns.step=cs.step+e[i].w; q.push(ns); } } } if(k>0)printf("-1\n"); } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); } scanf("%d%d%d",&S,&T,&k); if(S==T)k++; dijkstra(); Astar(); return 0; }二:简单k短路问题:【scoi2007】kshort
还是要求k短路。。只不过每条路线都必须不经过重复的点。。用刚才的方法就不能保证是简单路了(或者说考虑状压?可能也行。。但我没试过)
关于这个有一个专门的偏离算法。。。不过我没学
就说说当点比较少的时候的情况,比如这道题。
基本的思想就是二分路径长度,每次搜索只找到有多少存在条长度小于二分值mid的路径(需要知道的是有没有k条或以上这样的路径,所以如果已经找到的k条那么就不要继续搜索了)。。。
(于是当路径值大于mid时就剪枝。。)这里还是可以借鉴上面一个问题的某个地方。。。算出每个点到目标点的最短路来剪枝。
二分出求出最后的mid之后。我们便知道要求的路径的长度一定为mid。
于是我们再通过一次搜索求出长度小于等于mid-1的路径有x条,那么我们要求的路径就转换为长度为mid,字典序为第k-x小的路径。
那么就直接再搜一次,每次都先走最小能到的点,然后走到终点长度为mid的边才统计。一直走到第k-x个被统计的路径。。。这个路径,就是答案,输出就完了
有个比较坑的特殊数据要这样才能过:由于一开始求出的那个最短路没有考虑有些点不能走。。。所以每搜到一个dfs节点就要重新计算没经过已走点的最短路,然后再用来剪枝。。这样判断的代价非常高,所以当前点能过了,但其他点的时间通通大幅增加,最后一个点还会T。。不过BZOJ上算的是总时间。。。所以也就水过了。(这种高代价剪枝。。考场上用风险太大了)
代码实际上用了mato神犇说的迭代变优搜索:http://www.cppblog.com/MatoNo1/archive/2012/09/23/191705.html
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; const int inf=~0u >> 2; int n,m,k,a,b; int ans=0; int nextlenth=inf,LENTH; int w[50+10][50+10]; int rev[100+10][100+10]; bool con[100+10][100+10]; bool vis[100+10]; int head[100+10],tail=0; int head2[50+10],tail2=0,revdis[50+10]; bool in[50+10]; int lastlen=1; bool last=false; struct E { int v,w,next; }e[10000+10],e2[10000+10]; struct S { int dis; int len; int order[100+10]; bool operator <(const S &b)const { if(dis!=b.dis)return dis<b.dis; for(int i=0;i<min(len,b.len);i++)if(order[i]!=b.order[i])return order[i]<b.order[i]; return len<b.len; } }now; void spfa() { memset(revdis,0x3f,sizeof(revdis)); revdis[b]=0; queue<int>q; q.push(b); in[b]=true; while(!q.empty()) { int x=q.front(); q.pop(); in[x]=false; for(int i=head2[x];i!=-1;i=e2[i].next) { int v=e2[i].v; if(!vis[v]) { if(revdis[v]>revdis[x]+e2[i].w) { revdis[v]=revdis[x]+e2[i].w; if(!in[v])q.push(v),in[v]=true; } } } } } void add(int u,int v,int l) { e[tail].v=v;e[tail].w=l;e[tail].next=head[u]; head[u]=tail++; e2[tail2].v=u;e2[tail2].w=l;e2[tail2].next=head2[v]; head2[v]=tail2++; w[u][v]=min(w[u][v],l); rev[v][u]=min(rev[v][u],l); con[u][v]=1; } void dfs(int u,int dis) { if(u==b){ans++;return;} if(ans>=k)return; if(!con[u][b])return; if(dis+rev[b][u]>LENTH) { if(nextlenth>dis+rev[b][u])nextlenth=dis+rev[b][u]; return; } vis[u]=false; spfa(); if(dis+revdis[u]>LENTH) { if(nextlenth>dis+revdis[u])nextlenth=dis+revdis[u]; return; } vis[u]=true; for(int i=head[u];i!=-1;i=e[i].next) { if(ans>=k)return; int v=e[i].v; if(!vis[v]) { if(dis+e[i].w<=LENTH) { vis[v]=true; dfs(v,dis+e[i].w); vis[v]=false; } else { if(nextlenth>dis+e[i].w+rev[b][e[i].v])nextlenth=dis+e[i].w+rev[b][e[i].v]; } } } } void dfs2(int u,int dis) { if(u==b) { if(dis<=LENTH&&dis>lastlen)ans++; if(ans==k) { printf("%d",now.order[0]); for(int i=1;i<now.len;i++)printf("-%d",now.order[i]); printf("\n"); exit(0); } return; } if(!con[u][b])return; if(dis+rev[b][u]>LENTH) { return; } vis[u]=false; spfa(); if(dis+revdis[u]>LENTH) { return; } vis[u]=true; for(int i=1;i<=n;i++) { if(!vis[i]&&dis+w[u][i]<=LENTH) { vis[i]=true; now.order[now.len]=i; now.len++; dfs2(i,dis+w[u][i]); now.len--; vis[i]=false; } } } int main() { memset(head,-1,sizeof(head)); memset(head2,-1,sizeof(head2)); memset(w,0x3f,sizeof(w)); memset(rev,0x3f,sizeof(rev)); scanf("%d%d%d%d%d",&n,&m,&k,&a,&b); for(int i=1;i<=n;i++)w[i][i]=rev[i][i]=0,con[i][i]=1; for(int i=1;i<=m;i++) { int u,v,l; scanf("%d%d%d",&u,&v,&l); add(u,v,l); // add(v,u,l); } for(int k1=1;k1<=n;k1++) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { rev[i][j]=min(rev[i][j],rev[i][k1]+rev[k1][j]); con[i][j]|=con[i][k1]&con[k1][j]; } } // int l=1,r=50; // while(l<r) // { // ans=0; // LENTH=(l+r)>>1; // vis[a]=true; // now.len=1;now.order[0]=a; // dfs(a,0); // if(ans>=k)break; // else l=LENTH+1; // } for(LENTH=1;ans<k&&LENTH!=inf;lastlen=LENTH,LENTH=nextlenth) { ans=0; nextlenth=inf; memset(vis,0,sizeof(vis)); vis[a]=true; dfs(a,0); if(ans>=k)break; } if(ans<k) printf("No\n"); else { int anslen=LENTH; LENTH=lastlen; ans=0; memset(vis,0,sizeof(vis)); vis[a]=true; dfs(a,0); k=k-ans; ans=0; memset(vis,0,sizeof(vis)); vis[a]=true; LENTH=anslen; now.len=1; now.order[0]=a; dfs2(a,0); } return 0; }
相关文章推荐
- [HAOI2005]路由问题,第二短路
- 最短路问题
- 笔记③:POJ Roadblocks 次短路问题代码解析(优先队列逆序排列两种方法)
- 最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)
- HDU 3790 最短路径问题(迪杰斯特拉/优先队列/最短路)
- HDU 3790 最短路径问题 (最短路变形
- BFS(最短路+路径打印) POJ 3984 迷宫问题
- 匈牙利游戏(次短路问题)
- 最短路中部分点只能从中任意选取K个问题
- 最短路经问题
- 51Nod 迷宫问题(最短路+权值)(模板)
- 最短路Dijkstra算法的一些扩展问题
- Winform,窗口的关闭问题,暂时短路了....
- 【codevs1912】汽车加油行驶问题 分层图最短路
- kuangbin 最短路 H题(如何将看似拓扑的问题转化成floyd )
- ACM一类方程问题的求解[最短路建模] bzoj2118
- 次短路问题
- 硬币问题——固定终点的最长路和最短路
- 关于逻辑运算符、赋值运算符、位运算符的优先级,还有短路问题。
- 图论:最短路问题 Floyd Dijkstra SPFA算法