PAT-ADVANCED1072——Gas Station

版权声明:我的GitHub:https://github.com/617076674。真诚求星! https://blog.csdn.net/qq_41231926/article/details/83546826

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805396953219072

题目描述:

题目翻译:

1072 加油站

加油站的位置选取,应该使得其与任何住宅之间的最小距离尽可能远。同时又必须保证所有的居民都在自己的服务范围内。

现在给你一张城市的地图和几个加油站的候选位置,你需要给出最好的建议。如果有超过1种方案,选取离所有住宅平均距离最小的方案。如果方案还不唯一,输出编号最小的加油站位置。

输入格式:

每个输入文件包含一个测试用例。在每个测试用例中,第一行给出4个正整数:N(<= 1000),代表房子数量;M(<= 10),代表加油站的候选位置总数;K(<= 10000),代表连接加油站和房子的道路数目;Ds,代表加油站的最大服务距离。所有房子的编号为1 ~ N。所有加油站的候选位置编号从G1 ~ GM。

接下来的K行,每行以下述形式描述一条道路:

P1 P2 Dist

P1和P2代表道路的两端点,两端点均既有可能是房子编号也有可能是加油站的候选位置编号,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

知识点:Dijkstra算法、Bellman-Ford算法、SPFA算法

思路一:Dijkstra算法(邻接表实现)

首先是输入的节点标号与加油站编号全部要转换成数字编号。我假设房子的编号为1 ~ N,加油站的编号为N + 1 ~ N + M。由于输入的道路两端有可能是数字也有可能是G开头的字符串,我们统一用字符串接收,并设置一个函数change()将接收的字符串转换为数字编号。

我的转换规则是对G开头的字符串,取其后面一个字符与字符'0'作差,再返回N + 差值。但是这样做忽略了G10的情况,因此,当G开头的字符串长度是3位时,我们需要返回N + 10。

而我一开始所犯的错误是,忽略了N的范围,N最大可以到达1000。所以这个编号不止有一个字符,我一开始只考虑了N是个位数的情况。因此我们需要用头文件<sstream>中的stringstream类型将数字字符串转换成数字。

对每个加油站都用Dijkstra算法求其到各个房子的最小距离。

对每个加油站点,首先判断其到每个房子的最小距离是否小于等于Ds,如果有任何一个值大于Ds,这个加油站点是不可取的。

其次,对可取的加油站点进行筛选。

(1)筛选条件一:与任何住宅之间的最小距离尽可能远。

(2)筛选条件二:选取离所有住宅平均距离最小的方案。由于住宅总量一定,因此只需选取离所有住宅总距离最小的方案即可。

(3)筛选条件三:输出编号最小的加油站位置。这个条件很容易满足,我们只需要按编号从小到大遍历即可,对于住宅总距离相等的后续方案不考虑。

注意,在发现离住宅更远的最小距离后,我们不仅要更新离住宅更远的最小距离值,还需要更新离所有住宅的总距离

结果需要四舍五入,我的做法是将离所有住宅的总距离先乘以10再除以N,得到一个浮点数类型。再按同样的做法,不过这次得到的是一个int类型,将两者相减,如果差大于等于0.5,就将后面得到的int类型增1,最后输出结果时还需要再除以10,并保留1位小数。

时间复杂度是O(M * (N + M) ^ 2)。空间复杂度是O(N + M + K)。

C++代码:

#include<iostream>
#include<vector>
#include<string>
#include<sstream>

using namespace std;

struct node {
	int v;	//节点编号
	int len;	//道路长度
	node(int _v, int _len) : v(_v), len(_len) {}	//构造函数
};

int N;	//房子数量
int M;	//加油站数量
int K;	//道路数量
int Ds;	//加油站服务范围
int INF = 1000000000;	//无穷大数
vector<node> graph[1020]; //无向图,房子的编号为1 ~ N,加油站的编号为N + 1 ~ N + M
int d[1020];	//记录最短长度
bool visited[1020];	//标记数组

int totalDistance();
bool validPosition();
int minDistance();
void init();
int change(string s);
void dijkstra(int s);

int main() {
	cin >> N >> M >> K >> Ds;
	string P1, P2;
	int Dist;
	for(int i = 0; i < K; i++) {
		cin >> P1 >> P2 >> Dist;
		int id1 = change(P1);
		int id2 = change(P2);
		graph[id1].push_back(node(id2, Dist));
		graph[id2].push_back(node(id1, Dist));
	}
	int minLen = 0;
	int minTotalLen = INF;
	int result = 0;
	for(int i = N + 1; i <= N + M; i++) {
		dijkstra(i);
		if(validPosition()) {
			if(minDistance() > minLen) {
				result = i;
				minLen = minDistance();
				minTotalLen = totalDistance();	//此处也要更新minTotalLen 
			} else if(minDistance() == minLen) {
				if(minTotalLen > totalDistance()) {
					result = i;
					minTotalLen = totalDistance();
				}
			}
		}
	}
	if(result == 0){
		cout << "No Solution" << endl;
		return 0;
	}
	cout << "G" << result - N << endl;
	double average = minTotalLen * 10.0 / N;
	int averageResult = minTotalLen * 10 / N;
	if(average - averageResult >= 0.5){
		averageResult++;
	}
	printf("%.1lf %.1lf\n", minLen * 1.0, averageResult * 1.0 / 10);
	return 0;
}

int totalDistance() {
	int sum = 0;
	for(int i = 1; i <= N; i++) {
		sum += d[i];
	}
	return sum;
}

bool validPosition() {
	for(int i = 1; i <= N; i++) {
		if(d[i] > Ds) {
			return false;
		}
	}
	return true;
}

int minDistance() {
	int min = 1;
	for(int i = 2; i <= N; i++) {
		if(d[i] < d[min]) {
			min = i;
		}
	}
	return d[min];
}

void init() {
	for(int i = 1; i <= N + M; i++) {
		d[i] = INF;
		visited[i] = false;
	}
}

int change(string s) {
	if(s[0] == 'G'){
		if(s.length() == 3){
			return 10 + N;
		}else{
			int num = s[1] - '0';
			return num + N;
		}
	}else{
		stringstream ss;
		ss << s;
		int result;
		ss >> result;
		return result;
	}
}

void dijkstra(int s) {
	init();
	d[s] = 0;
	for(int i = 0; i < N + M; i++) {
		int u = -1, min = INF;
		for(int j = 1; j <= N + M; j++) {
			if(!visited[j] && min > d[j]) {
				min = d[j];
				u = j;
			}
		}
		if(u == -1) {
			return;
		}
		visited[u] = true;
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int len = graph[u][j].len;
			if(!visited[v]) {
				if(d[u] + len < d[v]) {
					d[v] = d[u] + len;
				}
			}
		}
	}
}

C++解题报告:

思路二:Bellman-Ford算法(邻接表实现)(测试点4会超时)

时间复杂度是O(M * K * (N + M))。空间复杂度是O(N + M + K)。

C++代码:

#include<iostream>
#include<vector>
#include<string>
#include<sstream>

using namespace std;

struct node {
	int v;	//节点编号
	int len;	//道路长度
	node(int _v, int _len) : v(_v), len(_len) {}	//构造函数
};

int N;	//房子数量
int M;	//加油站数量
int K;	//道路数量
int Ds;	//加油站服务范围
int INF = 1000000000;	//无穷大数
vector<node> graph[1020]; //无向图,房子的编号为1 ~ N,加油站的编号为N + 1 ~ N + M
int d[1020];	//记录最短长度

int totalDistance();
bool validPosition();
int minDistance();
void init();
int change(string s);
bool bellmanFord(int s);

int main() {
	cin >> N >> M >> K >> Ds;
	string P1, P2;
	int Dist;
	for(int i = 0; i < K; i++) {
		cin >> P1 >> P2 >> Dist;
		int id1 = change(P1);
		int id2 = change(P2);
		graph[id1].push_back(node(id2, Dist));
		graph[id2].push_back(node(id1, Dist));
	}
	int minLen = 0;
	int minTotalLen = INF;
	int result = 0;
	for(int i = N + 1; i <= N + M; i++) {
		bellmanFord(i);
		if(validPosition()) {
			if(minDistance() > minLen) {
				result = i;
				minLen = minDistance();
				minTotalLen = totalDistance();	//此处也要更新minTotalLen
			} else if(minDistance() == minLen) {
				if(minTotalLen > totalDistance()) {
					result = i;
					minTotalLen = totalDistance();
				}
			}
		}
	}
	if(result == 0) {
		cout << "No Solution" << endl;
		return 0;
	}
	cout << "G" << result - N << endl;
	double average = minTotalLen * 10.0 / N;
	int averageResult = minTotalLen * 10 / N;
	if(average - averageResult >= 0.5) {
		averageResult++;
	}
	printf("%.1lf %.1lf\n", minLen * 1.0, averageResult * 1.0 / 10);
	return 0;
}

int totalDistance() {
	int sum = 0;
	for(int i = 1; i <= N; i++) {
		sum += d[i];
	}
	return sum;
}

bool validPosition() {
	for(int i = 1; i <= N; i++) {
		if(d[i] > Ds) {
			return false;
		}
	}
	return true;
}

int minDistance() {
	int min = 1;
	for(int i = 2; i <= N; i++) {
		if(d[i] < d[min]) {
			min = i;
		}
	}
	return d[min];
}

void init() {
	for(int i = 1; i <= N + M; i++) {
		d[i] = INF;
	}
}

int change(string s) {
	if(s[0] == 'G') {
		if(s.length() == 3) {
			return 10 + N;
		} else {
			int num = s[1] - '0';
			return num + N;
		}
	} else {
		stringstream ss;
		ss << s;
		int result;
		ss >> result;
		return result;
	}
}

bool bellmanFord(int s) {
	init();
	d[s] = 0;
	for(int i = 0; i < N + M - 1; i++) {
		for(int u = 1; u <= N + M; u++) {
			for(int j = 0; j < graph[u].size(); j++) {
				int v = graph[u][j].v;
				int len = graph[u][j].len;
				if(d[u] + len < d[v]) {
					d[v] = d[u] + len;
				}
			}
		}
	}
	for(int u = 1; u <= N + M; u++) {
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int len = graph[u][j].len;
			if(d[u] + len < d[v]) {
				return false;
			}
		}
	}
	return true;
}

C++解题报告:

思路三:SPFA算法(邻接表实现)

期望时间复杂度是O(k * M * (N + M)),其中k是一个常数,在很多情况下k不超过2,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。空间复杂度是O(N + M + K)。

C++代码:

#include<iostream>
#include<vector>
#include<string>
#include<sstream>
#include<queue>

using namespace std;

struct node {
	int v;	//节点编号
	int len;	//道路长度
	node(int _v, int _len) : v(_v), len(_len) {}	//构造函数
};

int N;	//房子数量
int M;	//加油站数量
int K;	//道路数量
int Ds;	//加油站服务范围
int INF = 1000000000;	//无穷大数
vector<node> graph[1020]; //无向图,房子的编号为1 ~ N,加油站的编号为N + 1 ~ N + M
int d[1020];	//记录最短长度
bool inq[1020];
int countInq[1020];

int totalDistance();
bool validPosition();
int minDistance();
void init();
int change(string s);
bool spfa(int s);

int main() {
	cin >> N >> M >> K >> Ds;
	string P1, P2;
	int Dist;
	for(int i = 0; i < K; i++) {
		cin >> P1 >> P2 >> Dist;
		int id1 = change(P1);
		int id2 = change(P2);
		graph[id1].push_back(node(id2, Dist));
		graph[id2].push_back(node(id1, Dist));
	}
	int minLen = 0;
	int minTotalLen = INF;
	int result = 0;
	for(int i = N + 1; i <= N + M; i++) {
		spfa(i);
		if(validPosition()) {
			if(minDistance() > minLen) {
				result = i;
				minLen = minDistance();
				minTotalLen = totalDistance();	//此处也要更新minTotalLen
			} else if(minDistance() == minLen) {
				if(minTotalLen > totalDistance()) {
					result = i;
					minTotalLen = totalDistance();
				}
			}
		}
	}
	if(result == 0) {
		cout << "No Solution" << endl;
		return 0;
	}
	cout << "G" << result - N << endl;
	double average = minTotalLen * 10.0 / N;
	int averageResult = minTotalLen * 10 / N;
	if(average - averageResult >= 0.5) {
		averageResult++;
	}
	printf("%.1lf %.1lf\n", minLen * 1.0, averageResult * 1.0 / 10);
	return 0;
}

int totalDistance() {
	int sum = 0;
	for(int i = 1; i <= N; i++) {
		sum += d[i];
	}
	return sum;
}

bool validPosition() {
	for(int i = 1; i <= N; i++) {
		if(d[i] > Ds) {
			return false;
		}
	}
	return true;
}

int minDistance() {
	int min = 1;
	for(int i = 2; i <= N; i++) {
		if(d[i] < d[min]) {
			min = i;
		}
	}
	return d[min];
}

void init() {
	for(int i = 1; i <= N + M; i++) {
		d[i] = INF;
		inq[i] = false;
		countInq[i] = 0; 
	}
}

int change(string s) {
	if(s[0] == 'G') {
		if(s.length() == 3) {
			return 10 + N;
		} else {
			int num = s[1] - '0';
			return num + N;
		}
	} else {
		stringstream ss;
		ss << s;
		int result;
		ss >> result;
		return result;
	}
}

bool spfa(int s) {
	init();
	d[s] = 0;
	queue<int> q;
	q.push(s);
	inq[s] = true;
	countInq[s]++;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int j = 0; j < graph[u].size(); j++){
			int v = graph[u][j].v;
			int len = graph[u][j].len;
			if(len + d[u] < d[v]){
				d[v] = len + d[u];
				if(!inq[v]){
					q.push(v);
					inq[v] = true;
					countInq[v]++;
					if(countInq[v] > M + N){
						return false;
					}
				}
			}
		}
	}
	return true;
}

C++解题报告:

猜你喜欢

转载自blog.csdn.net/qq_41231926/article/details/83546826