垃圾箱分布 (30分)

大家倒垃圾的时候,都希望垃圾箱距离自己比较近,但是谁都不愿意守着垃圾箱住。所以垃圾箱的位置必须选在到所有居民点的最短距离最长的地方,同时还要保证每个居民点都在距离它一个不太远的范围内。

现给定一个居民区的地图,以及若干垃圾箱的候选地点,请你推荐最合适的地点。如果解不唯一,则输出到所有居民点的平均距离最短的那个解。如果这样的解还是不唯一,则输出编号最小的地点。

输入格式:

输入第一行给出4个正整数:N(≤10​3​​)是居民点的个数;M(≤10)是垃圾箱候选地点的个数;K(≤10​4​​)是居民点和垃圾箱候选地点之间的道路的条数;D​S​​是居民点与垃圾箱之间不能超过的最大距离。所有的居民点从1到N编号,所有的垃圾箱候选地点从G1到GM编号。

随后K行,每行按下列格式描述一条道路:

P1 P2 Dist

其中P1P2是道路两端点的编号,端点可以是居民点,也可以是垃圾箱候选点。Dist是道路的长度,是一个正整数。

输出格式:

首先在第一行输出最佳候选地点的编号。然后在第二行输出该地点到所有居民点的最小距离和平均距离。数字间以空格分隔,保留小数点后1位。如果解不存在,则输出No Solution

输入样例1:

4 3 11 5
1 2 2
1 4 2
1 G1 4
1 G2 3
2 3 2
2 G2 1
3 4 2
3 G3 2
4 G1 3
G2 G1 1
G3 G2 2

输出样例1:

G1
2.0 3.3

输入样例2:

2 1 2 10
1 G1 9
2 G1 20

输出样例2:

No Solution

注意数据的存储,输入道路两端点的编号要用string来接收,利用atoi()函数将字符串转为int存储

对于G1、G2等垃圾箱的编号,也转为int,新编号 = 垃圾箱的编号(不要G字符) + 居民的个数

然后迪杰斯特拉即可,但要注意,由于要进行多次迪杰斯特拉,因此初始化时要在迪杰斯特拉函数内,每进行一次迪杰斯特拉都要初始化一次,我刚开始写这道题时由于没有在迪杰斯特拉函数内部对vis[]函数初始化,运行答案正确,提交时只得了两分,找了好久的Bug

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10001;
const int INF = 0x3f3f3f;
int a[MAXN][MAXN];
int dist[MAXN];
int n,m,k,d;
struct node{
	int id;
	double minDist,avgDist;
}ans[11];
int cnt = 0;
void djkstla(int s){
	/* 由于djkstla函数不只执行一次, 因此每次都要初始化标记数组 */
	bool vis[MAXN]={false};
	fill(dist,dist+MAXN,INF);
	dist[s] = 0;
	for(int i=1;i<=n+m;i++){
		int min = INF,u = -1;
		for(int j=1;j<=n+m;j++){
			if(vis[j]==false && dist[j]<min){
				min = dist[j];
				u = j;
			}
		}
		if(u==-1) return ;
		vis[u] = true;
		for(int j=1;j<=n+m;j++){
			if(vis[j]==false && dist[j] > dist[u] + a[u][j]){
				dist[j] = dist[u] + a[u][j];
			}
		}
	}
	double sumD = 0;
	int minD = INF;
	for(int i=1;i<=n;i++){
		if(dist[i] > d) return ;
		minD = min(minD,dist[i]);
		sumD += dist[i];
	}
	ans[cnt].id = s;
	ans[cnt].minDist = minD; 
	ans[cnt].avgDist = sumD / n;
	cnt++;
}
int cmp(node a,node b){
	if(a.minDist!=b.minDist){
		return a.minDist > b.minDist;
	}else if(a.avgDist!=b.avgDist){
		return a.avgDist < b.avgDist;
	}else{
		return a.id < b.id;
	}
}
int main(){
	string s1,s2;
	int num;
	cin>>n>>m>>k>>d;
	for(int i=1;i<=n+m;i++){ //初始化a数组 
		for(int j=1;j<=n+m;j++){
			if(i!=j) a[i][j] = a[j][i] =  INF;
			else a[i][j] = 0; 
		} 
	}
	for(int i=0;i<k;i++){
		int n1=0,n2=0; 
		cin>>s1>>s2>>num;
		if(s1[0] == 'G') {
			s1 = s1.substr(1);
			n1 = n;
		}
		n1 += atoi(s1.c_str());
		if(s2[0] == 'G'){
			s2 = s2.substr(1);
			n2 = n;
		}
		n2 += atoi(s2.c_str());
		a[n1][n2] = a[n2][n1] = num;
	}
	for(int i=n+1;i<=n+m;i++){
		djkstla(i);	
	}
	sort(ans,ans+cnt,cmp);
	if(cnt!=0){
		printf("G%d\n",ans[0].id-n);
		printf("%.1f %.1f\n",ans[0].minDist,ans[0].avgDist);
	}else{
		printf("No Solution\n");
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值