您的位置:首页 > 其它

两种K短路问题

2014-02-23 16:31 155 查看
一:普通的K短路问题。

这个就是在一张图里面求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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: