图论最短路径题目题解

1.牧场行走

题目:

有\(N(2≤N≤1000)\)头奶牛.编号为1到N.它们正在同样编号为I到N的牧场上行走。为了方便.我们假设编号为i的牛恰好在第i号牧场上。有一些牧场间每两个牧场用一条双向道路相连,道路总共有N-1条。

奶牛可以在这些道路上行走,第i条道路把第\(A_i\)个牧场和第\(B_i\)个牧场连了起来\((1<=A_i<=N;1<=B_i<=N)\)。而它的长度是\(L_i(1<=L_i<=10000)\)。在任意两个牧场间,有且仅有一条由若千道路组成的路径相连,也就是说,所有的道路构成了一棵树。奶牛们十分希望经常互相见面,它们十分着急,所以希望你帮助它们计划它们的行程。你只需要计算出\(Q(1≤Q≤1000)\)对点之间的路径长度.每对点以一个询问\(p1,p2(1<=p1<=N;1<=p2<=N)\)的形式给出。

分析:

如果我们用普通的Floyd算法和Dijkstra算法来计算,如果评测系统较慢,会有时间超限的风险,这里说了牧场所构成的图是一棵树,我们可以想一个算法针对树这种特殊图。因为最短路径只有一条,所以只要求出两个点共同的祖先就可以了。然后可以发现从开始的点向前遍历,每次遍历这个点的父亲,把遍历到的点标记,标记这个点到原来的点的距离,然后再从结束的点向前遍历,每次遍历这个点的父亲,如果走到了被标记的点就说明找到了第一个共同的祖先,将两个点分别到找到的点距离加起来即可。

程序:

c++ 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,q,f[1001],a[1001],b[1001],d[1001];
vector<int> v1[1001],v2[1001];
void za(int x,int y)
{
	f[x]=y;
	for(int i=0;i<v1[x].size();i++)
		if(v1[x][i]!=y)
		{
			d[v1[x][i]]=v2[x][i];
			za(v1[x][i],x);
		}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>q;
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		v1[x].push_back(y);
		v1[y].push_back(x);
		v2[x].push_back(z);
		v2[y].push_back(z);
	}
	za(1,0);
	while(q--)
	{
		int x,y;
		cin>>x>>y;
		int sum=0;
		memset(b,0,sizeof(b));
		while(x)
		{
			b[x]=1;
			a[x]=sum;
			sum+=d[x];
			x=f[x];
		}
		sum=0;
		while(!b[y])
		{
			sum+=d[y];
			y=f[y];
		}
		cout<<sum+a[y]<<'\n';
	}
	return 0;
}

2.分糖果

题目:

童年的我们,将和朋友分享美好的事物作为自己的快乐。这天,C小朋友得到了Plenty of candies,将要把这些糖果分给要好的朋友们。已知糖果从一个人传给另一个人需要1 秒的时间,同一个小朋友不会重复接受糖果。由于糖果足够多,如果某时刻某小朋友接受了糖果,他会将糖果分成若干份,分给那些在他身旁且还没有得到糖果的小朋友们,而且自己会吃一些糖果。由于嘴馋,小朋友们等不及将糖果发完,会在得到糖果后边吃边发。每个小朋友从接受糖果到吃完糖果需要m秒的时间。那么,如果第一秒C小朋友开始发糖,第多少秒所有小朋友都吃完了糖呢?

题目条件:

1.1<=n<=100000,p<=n*(n-1)/2。

分析:

因为每次把糖果给出去,代价是一样的,可以直接宽搜,宽搜后的最短时间再加上m即可。

程序:

c++ 复制代码
#include<bits/stdc++.h>
using namespace std;
vector<int> a[100001];
int n,m,s,k,d[100001],ans;
struct dd
{
	int x,step;
};
queue<dd> q;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>s>>k;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	q.push(dd{s,1});
	while(!q.empty())
	{
		dd t=q.front();
		for(auto i:a[t.x])
		{
			if(d[i])
				continue;
			d[i]=1;
			ans=max(ans,t.step+1);
			q.push(dd{i,t.step+1});
		}
		q.pop();
	}
	cout<<ans+k<<'\n';
	return 0;
}

3.最优乘车

题目:

H城是一个旅游胜地,每年都有成千上万的人前来观光。为了方便游客,巴士公司在各个旅游景点及宾馆、饭店等地都设置了巴士站并开通了一些单程巴士线路。每条单程巴士线路从某个巴士站出发,依次途经若干个巴士站,最终到达终点巴士站。一名游客最近到H城旅游,他很想去公园游玩。但如果从他所在的饭店没有一路巴士可以直接到达公园,则他可能需要先乘某一路巴士,坐几站,再下来换乘同一站台的另一路巴士,这样换乘几次后到达公园。现在用正数1,2,...,N给H城的所有的巴士站编号,约定这名游客所在饭店的巴士站编号为1...N,公园巴士站的编号为N。编写程序,帮助这名游客寻找一条最优乘车方案,使他在从饭店(编号为1)乘车到公园(编号为N)的过程中换车的次数最少。

题目条件:

1.\((1≤M≤100,1<N≤500)\)。

分析:

这道题首先需要建图,从一路换乘到另一路答案是需要加一的,那我们可以换个思路,求乘坐过的巴士线路的数量再减去1就好了,建图可以是一条线路中任意一个点到这个线路中其他的点需要1的代价,而相同的点到相同的点是0,用普通的最短路径的算法算出来再减去1就可以了。

程序:

c++ 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,m,a[501][501],b[501],c[101][501],f[501];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>m>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j)
				a[i][j]=1e9;
	for(int i=1;i<=m;i++)
	{
		cin>>c[i][0];
		for(int j=1;j<=c[i][0];j++)
			cin>>c[i][j];
		for(int j=1;j<c[i][0];j++)
			for(int p=j+1;p<=c[i][0];p++)
				a[c[i][j]][c[i][p]]=1;
	}
	for(int i=2;i<=n;i++)
		f[i]=1e9;
	for(int i=1;i<=n;i++)
	{
		int k=-1;
		for(int j=1;j<=n;j++)
			if(!b[j]&&(k==-1||f[j]<f[k]))
				k=j;
		if(f[k]==1e9)
			break;
		b[k]=1;
		for(int j=1;j<=n;j++)
			if(a[k][j]!=1e9&&!b[j])
				f[j]=min(f[j],f[k]+a[k][j]);
	}
	if(f[n]!=1e9)
		cout<<f[n]-1<<'\n';
	else
		cout<<"NO"<<'\n';
	return 0;
}

4.行程安排

题目:

农夫约翰在N个农场上都建立一个飞机场,并且规定第1个到第K个飞机场为中心站。现在有M条单向的航线,第i条航线是从农场\(u_i\)到农场\(v_i\),需要花费\(d_i\)的费用。现在有Q个单向的飞行计划,第i个计划是从农场\(a_i\)到农场\(b_i\),为了使得能够从\(a_i\)到\(b_i\),飞机可能不得不经过其他的一些农场(因为可能没有直接的航线 ,这里有一个要求是所有飞行计划所途经的农场必须至少有一个中心站(中心站也可以是起点和终点),这样导致的后果是可能没有一条满足条件的飞行线路能够满足第i个飞行计划。对于能够满足条件的飞行计划中,请帮助约翰计算所有有效的飞行计划的最小总代价。

题目条件:

1.\(1<=N<=200,1<=K<=100,1<=M<=10000\)。

2.\(1<=d_i<=1000000,1<=Q<=10000\)。

分析:

因为这题的点的数量比较小,可以先Floyd预处理出来任意一个点到另一个点的最短路径,然后算点对的时候枚举1到K,答案就是\(fa_i中心站+f中心站b_i\)。

程序:

c++ 复制代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,k,q,f[201][201],s,sum;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>k>>q;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j)
				f[i][j]=1e18;
	for(int i=1;i<=m;i++)
	{
		long long x,y,z;
		cin>>x>>y>>z;
		f[x][y]=min(f[x][y],z);
	}
	for(int p=1;p<=n;p++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				f[i][j]=min(f[i][j],f[i][p]+f[p][j]);
	for(int i=1;i<=q;i++)
	{
		long long x,y,ans=1e18;
		cin>>x>>y;
		for(int j=1;j<=k;j++)
			ans=min(ans,f[x][j]+f[j][y]);
		if(ans!=1e18)
		{
			sum+=ans;
			s++;
		}
	}
	cout<<s<<'\n';
	cout<<sum<<'\n';
	return 0;
}