PAT 1072 Gas Station

题目链接: https://www.patest.cn/contests/pat-a-practise/1072

思路:通读一遍下来,发现题目的意思就是在几个候选的建站点里选一个节点,让这个节点到各个居民点的距离满足:(按优先级条件递减)

1.到各个居民点的距离必须小于等于 加油站的最大服务距离Ds。

2.再满足1的加油站点中,优先选择min_dis最大的那个点。(就是说每一个候选点都能求出一个最短距离数组dis[],这个数组里的最小值就是min_dis,取几个候选站点中min_dis最大的那个作为站点)

3.如果2不唯一,则优先选择ave_dis最小的作为站点。

4.如果3不唯一,则优先选择标号最小的作为站点。

ps:刚开始没有考虑条件2,只过了一个case.

思路: 各个候选站点的最短路径用Dijstra,然后在对满足1的所有站点用sort排序,最后输出。

注意考虑最短路径的时候要考虑 居民点+其余候选站点。其对于某一候选站点,循环的次数为n+m。

另外这里对候选站点进行处理,G1标号分配为n+1,G2为n+2,...,以此类推。

代码:

#include 
#include 
#include 
using namespace std;
const int MAX = 1050;
const int INF = 6553600;
int n, m, k, ds;
int map[MAX][MAX];//MAX >= n+m;
int dis[MAX];
int vis[MAX];
//getIndex函数的作用是 把接收到的站点名 转换成 其标号值,注意居民点标号最大为1000,候选站点最大为G10,转换后为n+10.
int getIndex(char s[])
{
	int isStation = 0;
	int ret = 0, c = 1;
	if (s[0] == 'G')
	{
		for (int i = strlen(s) - 1; i >= 1; i--)
		{
			ret += c*(s[i] - '0');
			c *= 10;
		}
		ret += n;
	}
	else
	{
		for (int i = strlen(s) - 1; i >= 0; i--)
		{
			ret += c*(s[i] - '0');
			c *= 10;
		}
	}
	return ret;
}
//求最短路径
void Dijstra(int start)
{
	int min = INF;
	int k = start;
	dis[start] = 0;
	// 首先在不在集合U(集合U是已经求得其最短路径的点的集合)中点找出dis最小的一个点
	for (int j = 0; j <= n+m; j++) //循环求出到所有点的最短路径,这里第一次循环时k为起始点,然后下面就相当于初始化dis.
	{
		min = INF;
		for (int i = 1; i <= n+m; i++)
		{
			if (dis[i] < min && vis[i] == 0)
			{
				min = dis[i];
				k = i;
			}
		}
		vis[k] = 1; //将这个点加入集合U中,并修改其余点的dis.
		for (int i = 1; i <= n+m; i++)
		{
			if (vis[i] == 1) continue;
			if (dis[i] > dis[k] + map[k][i])
			{
				dis[i] = dis[k] + map[k][i];
			}
		}
	}
}
//初始化dis,v
void Init()
{
	for (int i = 1; i <= n+m; i++)
	{
		dis[i] = INF;
		vis[i] = 0;
	}
}
//用来存储结果的结构体,最后用C++ sort对其进行 多关键字排序(这个PAT经常考察)
struct Ans
{
	double ave;
	double min;
	int index;
}ans[15];
//已得到dis数组,求出其min_dist,ave_dist,判断是否满足条件1
bool Output(double &ave,double &min)
{
	bool notExceed = true;
	for (int i = 1; i <= n; i++)
	{
		if (dis[i] > ds)
		{
			notExceed = false;
			break;
		}
		if (dis[i] < min)
		{
			min = dis[i];
		}
		ave += dis[i];
	}
	ave /= n;
	return notExceed;

}
bool cmp(Ans a, Ans b)
{
	if (a.min != b.min) return a.min > b.min;
	if (a.ave != b.ave) return a.ave < b.ave;
	return a.index < b.index;
}
int main()
{
	//freopen("D://input2.txt","r",stdin);
	scanf("%d %d %d %d",&n,&m,&k,&ds);
	for (int i = 1; i <= n+m; i++)
	{
		for (int j = i; j <= n+m; j++)
		{
			map[i][j] = map[j][i] = INF;
		}
	}
	char a[10], b[10];
	int c = 0;
	for (int i = 0; i < k; i++)
	{
		scanf("%s %s %d",a,b,&c);
		int ia = getIndex(a);
		int ib = getIndex(b);
		//printf("%d to %d = %d\n",ia,ib,c);
		map[ia][ib] = map[ib][ia] = c;
	}
	double ave = 0.0, min = INF;
	int cnt = 0;
	bool hasAns = false;
	for (int i = 1; i <= m; i++)
	{
		Init();
		int start = i + n;
		Dijstra(start);
		ave = 0.0, min = INF;
		if (Output(ave, min))
		{
			ans[cnt].ave = ave;
			ans[cnt].min = min;
			ans[cnt].index = i;
			hasAns = true;
			cnt++;
		}
	}
	if (hasAns)
	{
		sort(ans,ans+cnt,cmp);
		printf("G%d\n",ans[0].index);
		printf("%.1f %.1f\n",ans[0].min,ans[0].ave);
	}
	else
	{
		printf("No Solution\n");
	}
	return 0;
}
总结:

从开始做题到写完代码大概花了30分钟,调试测试用例又磕磕绊绊花了30分钟。最后发现是没有考虑条件2,然后稍微修改一下就AC了。

对Dijstra的掌握仍然不够好,要理解其"贪心"的算法思想;重定向输入流真的是很好用,省去了测试用例输入的大把时间;

再接再厉吧。

你可能感兴趣的:(PAT题解)