分层图最短路

 

分层图最短路,就是在分层图上解决最短路问题
 第一种就是dp的思想:

一般模型为:
在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线
常规思路:
想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次......k次
这样实际我们可以把这k个点想象成对应dp的不同的状态
dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线
我们用to表示要到达的点,x表示父亲节点,就有
dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j - 1])
因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态
如果我们消耗了免费过路权
dis[to][j] = min{dis[x][j - 1]}
如果我们没消耗免费过路权
dis[to][j] = min{dis[x][j] + val(x, to)}
这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏
也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权
到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次....用了k次
也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了
然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路,这样可能有点抽象
实际上把这想象成一个dp的过程是最好理解的

第二种比较容易理解(真正的分层图):

容易卡内存

 

例题:

第一种做法(dp的思想):

题目描述
给定一张地图一共有 n 个城市,城市编号为 1 ~ n - 1,这 n 个城市通过 m
条铁路连接(走一条铁路视为乘车一次)。而小A 想从城市 s 出发,到 达城市 t
结束。小A 可以免费乘车 k 次,现在他想知道,他这 次旅游的最少花费是多少?
输入格式
第一包含三个整数 n, m, k,含义见题目描述。
第二有两个整数 s, t,表示小A 的出发城市和结束城市。
接下来 m 行,每行三个整数 x, y, z,表?在城市 x 和 y 之间有一条铁路相
连,乘车花费为 z。 
输出格式
输出一行,一个整数表示答案。
样例输入
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
样例输出
8
数据范围
对于 30% 的数据, 2 ≤ n ≤ 50, 1 ≤ m ≤ 300, k = 0;
对于 50% 的数据, 2 ≤ n ≤ 600, 1 ≤ m ≤ 6000, 0 ≤ k ≤ 1;

#include<bits/stdc++.h>
using namespace std;
 
#define e exp(1)
#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}

const int maxn=1e5+10;
int n,m,k,s,t;

struct qnode{
    int v,dis,level;
    qnode(int v=0,int dis=0,int level=0):v(v),dis(dis),level(level) {}
    bool operator<(const qnode &r)const
    {
        return dis>r.dis;
    }
};
struct Edge{
	int v,w;
	Edge(int v=0,int w=0):v(v),w(w) {} 
};
vector<Edge> ve[maxn];
ll d[maxn][11];
bool vis[maxn][11];

void addEdge(int u,int v,int w)
{
	ve[u].push_back(Edge(v,w));
}
void dijkstra(int start)
{
	mem(vis,false);
	mem(d,inf);
	priority_queue<qnode> q;
	d[start][0]=0;
	
	q.push(qnode(start,0,0));
	qnode tmp;
	while(!q.empty())
	{
		tmp=q.top();
		q.pop();
		int u=tmp.v;
		int lv=tmp.level;
		if(vis[u][lv])continue;
		vis[u][lv]=true;
		for(int i=0; i<ve[u].size(); i++)
		{
			int v=ve[u][i].v;
			int w=ve[u][i].w;
			
			if(w+d[u][lv]<d[v][lv])
			{
				d[v][lv]=w+d[u][lv];
				q.push(qnode(v,d[v][lv],lv));
			}
			
			if(lv<k)
			{
				if(d[u][lv]<d[v][lv+1])
				{
					d[v][lv+1]=d[u][lv];
					q.push(qnode(v,d[v][lv+1],lv+1));
				}
			}
		}
	}
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		scanf("%d%d",&s,&t);
		for(int i=0; i<=n; i++)ve[i].clear();
		for(int i=0; i<m; i++)
		{
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			addEdge(u,v,w);
			addEdge(v,u,w);
		}
		
		dijkstra(s);
		ll ans=d[t][0];
		for(int i=0; i<=k; i++)ans=min(ans,d[t][i]);
		printf("%lld\n",ans);
	}
	return 0;
}

第二种真正的分层:

ACM-ICPC 2018 南京赛区网络预赛 (计蒜客)

There are N cities in the country, and M directional roads from u to v(1≤u,v≤n). Every road has a distance ci​. Haze is a Magical Girl that lives in City 1, she can choose no more than K roads and make their distances become 0. Now she wants to go to City N, please help her calculate the minimum distance.

Input

The first line has one integer T(1≤T≤5), then following T cases.

For each test case, the first line has three integers N, M and K.

Then the following M lines each line has three integers, describe a road Ui​,Vi​,Ci​. There might be multiple edges between u and v.

It is guaranteed that N≤100000,M≤200000,K≤10,
0≤Ci​≤1e9. There is at least one path between City 1 and City N.

Output

For each test case, print the minimum distance.

解析:这里注意:它路不是双向的,是单向的

这里有一个点说明一下:vector在插入大数据和很多次插入时,会很浪费时间,因为和结构体联系一起了,在vector使用时,都需要调用自身内部函数。所以在大数据时,尽量减少vector使用,可以直接使用指针的方式来代替对vector操作。这就说明大数据容易超时,就像下面的代码,我就是用vector直接超时了。

超时代码:

#include<bits/stdc++.h>
using namespace std;
 
#define e exp(1)
#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}

const int maxn=5e6+5;

int n,m,k;

struct node{
	int v,w;
	node(int v,int w):v(v),w(w) {}
};

struct qnode{
	int to,dis;
	qnode(int to,int dis):to(to),dis(dis) {}
	bool operator <(const qnode &X)const
	{
		return dis>X.dis;
	}
};
ll d[maxn];
bool vis[maxn];
vector<node> ve[maxn];
void dijkstra(int s)
{
	priority_queue<qnode> q;
	mem(d,inf);
	mem(vis,false);

	d[s]=0;
	
	q.push(qnode(s,0));
	while(!q.empty())
	{
		qnode tmp=q.top();q.pop();
		int u=tmp.to;
		if(vis[u])continue;
		vis[u]=true;
		
		for(int i=0; i<ve[u].size(); i++)
		{
			int v=ve[u][i].v;
			int w=ve[u][i].w;
			if(d[v]>d[u]+w)
			{
				d[v]=d[u]+w;
				q.push(qnode(v,d[v]));
			}
		}
	}
}
void addnode(int u,int v,int w)
{
	ve[u].push_back(node(v,w));
}

int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&n,&m,&k);
		for(int i=0; i<maxn; i++)ve[i].clear();
		for(int i=0; i<m; i++)
		{
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			for(int j=0; j<=k; j++)
			{
				addnode(u+j*n,v+j*n,w);
				if(j!=k)
				addnode(u+j*n,v+(j+1)*n,0);
			}
		}
		
		dijkstra(1);
		
		ll ans=inf;
		for(int i=0; i<=k; i++)ans=min(ans,d[n+i*n]);
		printf("%lld\n",ans);
	}
	return 0;
}

用链表实现:

#include<bits/stdc++.h>
using namespace std;

#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}

const int maxn=1e5+5;
const int maxm=3e6+5;
int n,m,k,p=0,head[maxm];

struct node{
	int to,w,next;
}e[maxm<<1];
void addnode(int u,int v,int w)
{
	e[++p].to=v;e[p].w=w;e[p].next=head[u];head[u]=p;
}
struct qnode{
	int to,dis;
	qnode(int to,int dis):to(to),dis(dis) {}
	bool operator <(const qnode &X)const
	{
		return dis>X.dis;
	}
};
ll d[maxm];
bool vis[maxm];
void dijkstra(int s)
{
	priority_queue<qnode> q;
	mem(d,inf);
	mem(vis,false);

	d[s]=0;
	
	q.push(qnode(s,0));
	while(!q.empty())
	{
		qnode tmp=q.top();q.pop();
		int u=tmp.to;
		if(vis[u])continue;
		vis[u]=true;
		
		for(int i=head[u]; i; i=e[i].next)
		{
			int v=e[i].to;
			int w=e[i].w;
			if(d[v]>d[u]+w)
			{
				d[v]=d[u]+w;
				q.push(qnode(v,d[v]));
			}
		}
	}
}

int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		mem(head,0);p=0;
		scanf("%d%d%d",&n,&m,&k);
		for(int i=0; i<m; i++)
		{
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			for(int j=0; j<=k; j++)
			{
				addnode(u+j*n,v+j*n,w);
				if(j!=k)
				addnode(u+j*n,v+(j+1)*n,0);
			}
		}
		
		dijkstra(1);
		
		ll ans=inf;
		for(int i=0; i<=k; i++)ans=min(ans,d[n+i*n]);
		printf("%lld\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值