两种K短路问题

一:普通的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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值