预备队第八周训练题集

P8794 [蓝桥杯 2022 国 A] 环境治理

题目描述
LQ 国拥有 n 个城市,从 0 到 n−1 编号,这 n 个城市两两之间都有且仅有一条双向道路连接,这意味着任意两个城市之间都是可达的。每条道路都有一个属性 D,表示这条道路的灰尘度。当从一个城市 A 前往另一个城市 B 时,可能存在多条路线,每条路线的灰尘度定义为这条路线所经过的所有道路的灰尘度之和,LQ 国的人都很讨厌灰尘,所以他们总会优先选择灰尘度最小的路线。

LQ 国很看重居民的出行环境,他们用一个指标 P 来衡量 LQ 国的出行环境,

P 定义为:P= \sum_{i=0}^{n-1} \sum_{j=0}^{n-1}d(i,j)

表示城市 i 到城市 j 之间灰尘度最小的路线对应的灰尘度的值。

为了改善出行环境,每个城市都要有所作为,当某个城市进行道路改善时,会将与这个城市直接相连的所有道路的灰尘度都减少 1,但每条道路都有一个灰尘度的下限值 L,当灰尘度达到道路的下限值时,无论再怎么改善,道路的灰尘度也不会再减小了。

具体的计划是这样的:

第 1 天,0 号城市对与其直接相连的道路环境进行改善;
第 2 天,1 号城市对与其直接相连的道路环境进行改善;
……

第 n 天,n−1 号城市对与其直接相连的道路环境进行改善;
第 n+1 天,0 号城市对与其直接相连的道路环境进行改善;
第 n+2 天,1 号城市对与其直接相连的道路环境进行改善;
……

LQ 国想要使得 P 指标满足 P≤Q。请问最少要经过多少天之后,P 指标可以满足 P≤Q。如果在初始时就已经满足条件,则输出 0;如果永远不可能满足,则输出 −1。

输入格式
输入的第一行包含两个整数 n,Q,用一个空格分隔,分别表示城市个数和期望达到的 P 指标。

接下来 n 行,每行包含 n 个整数,相邻两个整数之间用一个空格分隔,其中第 i 行第 j 列的值 

D i,j (Di,j=Dj,i,  Di,i=0) 表示城市 i 与城市 j 之间直接相连的那条道路的灰尘度。

接下来 n 行,每行包含 n 个整数,相邻两个整数之间用一个空格分隔,其中第 i 行第 j 列的值 
L i,j (Li,j=L j,i,  Li,i=0) 表示城市 i 与城市 j 之间直接相连的那条道路的灰尘度的下限值。

输出格式
输出一行包含一个整数表示答案。

#include 
#define MAXN 105
using namespace std;

int n,q,cnt=-1;
int mp[MAXN][MAXN];
int f[MAXN][MAXN];
int mmp[MAXN][MAXN];

int check(int day)
{
	int res=0;
	for(int i =0; i=i+1?1:0);
		for(int j=0;jmmp[j][i]+mmp[i][k])
				mmp[j][k]=mmp[j][i]+mmp[i][k];
			}
		}
	}
	for(int i =0; i>n>>q;
	for(int i=0;i>mp[i][j];
	}
	for(int i=0;i>f[i][j];
	}
	int l=0,r=100000*n;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(check(mid)<=q)
		{
			r=mid-1;
			cnt=mid;
		}
		else   l=mid+1;
	}
	cout<

这道题从一开始就理解错题意了。。。一开始写的判断是如果f数组的加和大于q就直接输出-1。这个思路totally不正确,因为两个不相邻点之间的可以通过其他点作为中介进行连通。换句话说,就是通过中介连通的路的灰尘值可以小于两个点直接连通的灰尘值的下限。还有就是这道题的天数和灰尘值之间是单调递减的关系,因此可以用二分降低时间复杂度。

P1144 最短路计数


题目描述
给出一个 N 个顶点 M 条边的无向无权图,顶点编号为 1∼N。问从顶点 1 开始,到其他每个点的最短路有几条。

输入格式
第一行包含 2 个正整数 N,M,为图的顶点数与边数。

接下来 M 行,每行 2 个正整数 x,y,表示有一条由顶点 x 连向顶点 y 的边,请注意可能有自环与重边。

输出格式
共 N 行,每行一个非负整数,第 i 行输出从顶点 1 到顶点 i 有多少条不同的最短路,由于答案有可能会很大,你只需要输出 ans mod 100003 后的结果即可。如果无法到达顶点 i 则输出 0。

#include 
#define MAXN 1000005
using namespace std;

int ans[MAXN];
int vis[MAXN];
int dis[MAXN];
int n,m;

vector g[MAXN];
queue q;

void spfa(int s)
{
	for(int i=1;i<=n;i++)   dis[i]=0x7fffffff;
	vis[s]=1;
	dis[s]=0;
	ans[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int tmp=q.front();
		q.pop();
		for(int i=0;idis[tmp]+1)
			{
				dis[v]=dis[tmp]+1;
				ans[v]=ans[tmp];    //v的最短路的个数和tmp一样 
				if(!vis[v])
				{
					vis[v]=1;
					q.push(v);
				}
			}
			else if(dis[v]==dis[tmp]+1)    ans[v]=(ans[tmp]+ans[v])%100003;
		}
		vis[tmp]=0;
	}
} 

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);   //无边无权图要双向存 
	}
	spfa(1);
	for(int i=1;i<=n;i++)   cout<

这道题用spfa,崩溃了看了一个下午还没完全弄懂为什么最后vis[tmp]要等于0。在洛谷试了一下,把vis[tmp]=0注释掉还是AC了。这道题要注意无边无向图,所以要有向正存一次,反存一次。对于重边的情况,就是包含在dis[v]=dis[tmp]+1的情况里。 

P2661 [NOIP2015 提高组] 信息传递

题目描述
有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?

输入格式
输入共 2 行。

第一行包含 1 个正整数 n,表示 n 个人。

第二行包含 n 个用空格隔开的正整数 1,2,⋯,T1,T2,⋯,Tn,其中第 i 个整数 Ti 表示编号为 i 的同学的信息传递对象是编号为 Ti 的同学,Ti≤n 且 Ti≠i。

输出格式
共一行一个整数,表示游戏一共可以进行多少轮。

#include 
#define MAXN 200005
using namespace std;
int n,cnt;
int p[MAXN];
int ans=0x3f3f3f3f;

int find(int x,int &cnt)
{
	cnt++;
	if(p[x]==x)    return x;
	else   return  find(p[x],cnt);
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)    p[i]=i;
	for(int i=1;i<=n;i++)
	{
		cnt=0;
		int x;
		cin>>x;
		if(find(x,cnt)==i)   
		ans=min(ans,cnt);
		else   p[i]=x;
	}
	cout<

图论求最小环,利用并查集。cnt要加引用!!!因为cnt++后的值要传回来 

P4779 【模板】单源最短路径(标准版)

题目描述
给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。

数据保证你能从 s 出发到任意点。

输入格式
第一行为三个正整数 n,m,s。 第二行起 m 行,每行三个非负整数ui,vi,wi,表示从 ui 到 vi 有一条权值为 wi 的有向边。

输出格式
输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。

#include 
#define MAXN 1000005
using namespace std;

struct edge
{
	int to;
	int nxt;
	int weight;
}e[MAXN];

struct priority
{
	int ans;
	int id;
	bool operator <(const priority &x)const
	{
		return x.ans q;

void add(int u,int v,int w)
{
	e[++cnt].to=v;
	e[cnt].weight=w;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int main()
{
	cin>>n>>m>>s;
	for(int i=1;i<=n;i++)     ans[i]=2147483645;
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
	}
	ans[s]=0;
	q.push((priority{0,s}));
	while(!q.empty())
	{
		priority tmp=q.top();
		q.pop();
		int res=tmp.id;
		if(!vis[res])
		{
			vis[res]=1;
			for(int i=head[res];i;i=e[i].nxt)
			{
				if(ans[e[i].to]>ans[res]+e[i].weight)
				{
					ans[e[i].to]=ans[res]+e[i].weight;
					if(!vis[e[i].to])     q.push(priority{ans[e[i].to],e[i].to});
				}
			}
		}
	}
	for(int i=1;i<=n;i++)   cout<

这道题要用dijkstra的做法。这个算法在数据结构课上有接触过,当时存图是用了指针,这道题让我知道了链式向前星的存图方式,以及如何在结构体内用重载运算符来实现优先队列。整体思路就是从起点开始,不断更新其与其他节点的最短路径。在优先队列中找与之相连的边中边值最小的,访问它,重复上述过程。注意的是不能重复访问一个节点 

B3647 【模板】Floyd


题目描述
给出一张由 n 个点 m 条边组成的无向图。
求出所有点对 (i,j) 之间的最短路径。

输入格式
第一行为两个整数 n,m,分别代表点的个数和边的条数。

接下来 m 行,每行三个整数 u,v,w,代表 u,v 之间存在一条边权为 w 的边。

输出格式
输出 n 行,每行 n 个整数。
第 i 行的第 j 个整数代表从 i 到 j 的最短路径。

#include 
#define MAXN 105
using namespace std;

int a[MAXN][MAXN];
int n,m;

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(j==i)   a[i][j]=0;
			else a[i][j]=100005;
		}
	}
	for(int i=1;i<=m;i++)
	{
		int b,c,d;
		cin>>b>>c>>d;
		a[b][c]=d;
		a[c][b]=d;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
			{
				if(a[j][k]>a[j][i]+a[i][k])
                {
				    a[j][k]=a[j][i]+a[i][k];
                    a[k][j]=a[j][k];
                }
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cout<

你可能感兴趣的:(算法)