每日总结2023.2.5(邮递员送信、无线通讯网)

文章介绍了两个问题的解决方法:一是使用双Dijkstra算法寻找邮递员送信的最短时间;二是通过构建最小生成树确定无线通讯网的最小通话距离。这两个问题都涉及到图论中的算法,分别利用了路径优化和网络连通性理论。
摘要由CSDN通过智能技术生成

P1629 邮递员送信 https://www.luogu.com.cn/problem/P1629

题目描述

有一个邮递员要送东西,邮局在节点 1。他总共要送 n−1 样东西,其目的地分别是节点 2 到节点 n。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 m 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n−1 样东西并且最终回到邮局最少需要的时间。

输入格式

第一行包括两个整数,n 和 m,表示城市的节点数量和道路数量。

第二行到第(m+1) 行,每行三个整数u,v,w,表示从 u 到 v 有一条通过时间为 w 的道路。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

输入输出样例

输入 #1复制

5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2

输出 #1复制

83

说明/提示

对于 30% 的数据,1≤n≤200。

对于 100% 的数据,1≤n≤10^3,1≤m≤10^5,1≤u,v≤n,1≤w≤10^4,输入保证任意两点都能互相到达。

思路:双dijkstra。一个算由1指向其余各个顶点的,一个算其余各个顶点到1的。一个是正向的,一个是反向的。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define inf 1000000000
int u[100000+5];
int v[100000+5];
int w[100000+5];
int first1[1000+5];//first1[i]为i点出发的边
int first2[1000+5];//first2[i]为指向i的边
int nex1[200000+5];
int nex2[200000+5];
int dis1[100000+5];
int dis2[100000+5];
bool book1[100000+5];
bool book2[100000+5];
struct heap{
	int dis;
	int pos;
	bool operator<(const heap &b) const{
		return this->dis>b.dis;
	}
};//堆
priority_queue<heap>que1;
priority_queue<heap>que2;
void buide1()
{
	int k=first1[1];
	while(k!=-1)
	{
		que1.push((heap){w[k],v[k]});
		k=nex1[k];
	}
}
void buide2()
{
	int k=first2[1];
	while(k!=-1)
	{
		que2.push((heap){w[k],u[k]});
		k=nex2[k];
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		first1[i]=-1;
		first2[i]=-1;
		dis1[i]=inf;
		dis2[i]=inf;
	}
	dis1[1]=0;
	dis2[1]=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u[i],&v[i],&w[i]);
		nex1[i]=first1[u[i]];
		nex2[i]=first2[v[i]];
		first1[u[i]]=i;
		first2[v[i]]=i;
	}
	buide1();//构造以1为起点的优先队列
	buide2();//1为终点
	book1[1]=true;
	book2[1]=true;
	int ans=0;
	int near;
	int k;
	while(!que1.empty())
	{
		near=que1.top().pos;
		if(book1[near])
		{
			que1.pop();
			continue;
		}
		book1[near]=true;
		ans+=que1.top().dis;
		dis1[near]=que1.top().dis;
		k=first1[near];
		while(k!=-1)
		{
			if(dis1[v[k]]>dis1[near]+w[k])
			{
				que1.push((heap){dis1[near]+w[k],v[k]});
			}
			k=nex1[k];
		}
		que1.pop();
	}
	while(!que2.empty())
	{
		near=que2.top().pos;
		if(book2[near])
		{
			que2.pop();
			continue;
		}
		book2[near]=true;
		ans+=que2.top().dis;
		dis2[near]=que2.top().dis;
		k=first2[near];
		while(k!=-1)
		{
			if(dis2[u[k]]>dis2[near]+w[k])
			{
				que2.push((heap){dis2[near]+w[k],u[k]});
			}
			k=nex2[k];
		}
		que2.pop();
	}	
	printf("%d",ans);
	return 0;
}

P1991 无线通讯网https://www.luogu.com.cn/problem/P1991

题目描述

国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;

每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。

任意两个配备了一条卫星电话线路的哨所(两边都有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。

收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

输入格式

从 wireless.in 中输入数据第 1 行,2 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标(x,y),以 km 为单位。

输出格式

输出 wireless.out 中

第 1 行,1 个实数 D,表示无线电收发器的最小传输距离,精确到小数点后两位。

输入输出样例

输入 #1复制

2 4
0 100
0 300
0 600
150 750

输出 #1复制

212.13

说明/提示

对于 20% 的数据:P=2,S=1

对于另外20% 的数据:P=4,S=2

对于 100% 的数据保证:1≤S≤100,S<P≤500,0≤x,y≤10000。

思路:生成最小树。

  1. 建边,结构体edge,u->v的dis=√(u.x-v.x)^2+(u.y-v.y)^2;
  2. 排序,对所建边从小到大排序
  3. Kruskal
  4. 注意只需要p-s条边即可,剩下的用卫星电话

代码实现

#include<bits/stdc++.h>
using namespace std;
struct point{
	int x;
	int y;
}p[505];
struct edge{
	int u;
	int v;
	double dis;
}e[1000000];
void init(int *parent,int n)
{
	for(int i=0;i<=n;i++)
	{
		parent[i]=i;
	}
}
double dist(point a,point b)
{
	return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}
void quicksort(int l,int r)
{
	if(l>=r) return;
	int i=l;int j=r;
	edge temp;
	while(i!=j)
	{
		while(e[j].dis>=e[l].dis&&i<j)
		{
			j--;
		}
		while(e[i].dis<=e[l].dis&&i<j)
		{
			i++;
		}
		temp=e[i];
		e[i]=e[j];
		e[j]=temp;
	}
	temp=e[l];
	e[l]=e[i];
	e[i]=temp;
	quicksort(l,i-1);
	quicksort(i+1,r);
}
int find(int* parent,int x)
{
	while(parent[x]!=x)
	{
		parent[x]=parent[parent[x]];
		x=parent[x];
	}
	return x;
}
int link(int *parent,int x,int y)
{
	int x_root=find(parent,x);
	int y_root=find(parent,y);
	if(x_root==y_root)
	{
		return 0;
	}
	else
	{
		parent[x_root]=y_root;
		return 1;
	}
}
int main()
{
	int s,n;
	scanf("%d%d",&s,&n);
	int parent[505];
	init(parent,n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&p[i].x,&p[i].y);
	}
	int cnt=0;
	for(int i=1;i<=n;i++)//建边
	{
		for(int j=i+1;j<=n;j++)
		{
			cnt++;
			e[cnt].u=i;
			e[cnt].v=j;
			e[cnt].dis=dist(p[i],p[j]);
		}
	}
	quicksort(1,cnt);//排序
	int num=0;
	int flag;
	for(int i=1;i<=cnt;i++)
	{
		flag=link(parent,e[i].u,e[i].v);
		if(flag)
		{
			num++;//加入的边+1
		}
		if(num==n-s)//剩下的用卫星
		{
			printf("%.2lf",dist(p[e[i].u],p[e[i].v]));
			break;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值