题目链接: 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的掌握仍然不够好,要理解其"贪心"的算法思想;重定向输入流真的是很好用,省去了测试用例输入的大把时间;
再接再厉吧。