Google Kickstart2019 Round A Problem A Training (7pts, 13pts)
作为一名学校足球教练,你的任务是挑选一支由P个学生组成的团队代表你的学校。
共有N名学生供你挑选,第 i 名学生的技术等级为Si,这是一个正整数,表示他们的技术水平。
在你看来一个合理的团队中的P个球员的技术应该是相当的,这样才能使每个人都融入到队内。
在最开始,你可能无法直接选出一个配置合理的队伍,因此你将为一些学生提供一对一的辅导。
将一名学生的技术等级提高1需要你花费1个小时的时间来进行辅导。
比赛季很快就开始了(事实上,第一场比赛已经开始了!),所以你想知道训练出一个合理的团队,你需要提供的最少训练小时数是多少。
输入格式
第一行包含整数T,表示共有T组测试数据。
每组测试数据的第一行包含两个整数N和P。
第二行包含N个整数Si,其中第 i 个整数为第 i 个学生的技术等级。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为“Case #x: y”,其中x为组别编号(从1开始),y为所需的最少训练小时数。
数据范围
1≤T≤100,
1≤Si≤10000,
2≤P≤N,
2≤N≤105
输入样例:
3
4 3
3 1 9 100
6 2
5 5 1 2 3 4
5 5
7 7 1 7 7
输出样例:
Case #1: 14
Case #2: 0
Case #3: 6
样例解释
在样例#1中,你可以花费总共6个小时训练第一个学生,8个小时训练第二个学生,使得前三个学生的技能水平同为9。这是你可以花费的最短时间,因此答案是14。
在样例#2中,你可以选择直接选择前两个学生而无需进行任何指导,因此答案为0。
在样例#3中,P = N,因此每个学生都将加入您的团队。 你必须花6个小时训练第三个学生,这样所有人的技术等级都为7。 这是你可以花费的最短时间,所以答案是6。
#include <iostream>
#include <algorithm>
#include <limits.h>
using namespace std;
const int N = 100010;
int n, p;
int skill[N], sum[N]; //能力值和前缀和
int main()
{
int T;
cin >> T;
for(int C = 1; C <= T; C ++)
{
cin >> n >> p;
for(int i = 1; i <= n; i ++) cin >> skill[i]; //读入能力值
sort(skill + 1, skill + 1 + n);
for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + skill[i]; //求前缀和
int res = INT_MAX;
for(int i = p; i <= n; i ++)
res = min(res, p * skill[i] - (sum[i] - sum[i - p]));
printf("Case #%d: %d\n", C, res);
}
return 0;
}
Google Kickstart2019 Round A Problem B Parcels (15pts, 20pts)
恭喜你被聘为著名包裹投递公司的首席决策者(CDM)。
客户喜欢更快的物流,因此你决定在全球范围内降低包裹的运输时间,从而吸引更多客户。
你已经向当局介绍了这个想法,他们已经为你分配了足够的预算来建立一个新的运输处。
我们将世界划分为一个R×C的方格矩阵。
每个方格内或者存在一个运输处,或者没有。
你可以选择一个还未建立运输处的方格,在上面建立新的运输处。
如果某方格包含运输处,则包裹到该方格的运输时间为0。
否则,该方格的运输时间被定义为该方格与包含运输处的任何其他方格之间的最小曼哈顿距离。
总运输时间是所有方格的运输时间的最大值。
在你有权利建立一个新的运输处(最多一个)的情况下,总运输时间最短是多少?
注意:两个方格(r1,c1)和(r2,c2)之间的曼哈顿距离定义为| r1 - r2 | + | c1 - c2 |,其中| * |运算符表示绝对值。
输入格式
第一行包含整数T,表示共有T组测试数据。
每组测试数据第一行包含两个整数R和C,分别表示矩阵的行数和列数。
接下来R行,每行包含一个长度为C的只包含0或1的字符串,其中0表示没有运输处,1表示有运输处。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为“Case #x: y”,其中x为组别编号(从1开始),y为最小总运输时间。
数据范围
1≤T≤100,
1≤R,C≤250,
数据保证至少存在一个运输处
输入样例:
3
3 3
101
000
101
1 2
11
5 5
10001
00000
00000
00000
10001
输出样例:
Case #1: 1
Case #2: 0
Case #3: 2
样例解释
在样例#1中,通过在没有运输处的五个方格中的任何一个中建立新的运输处,你就可以获得最小总运输时间1。
在样例#2中,所有方格都已经有一个运输处,因此最小总运输时间为0。请注意,你可以选择不建立新的运输处。
在样例#3中,要获得最小的总运输时间2,你可以在以下任何方格中建立新的运输处:(2,3),(3,2),(3,3),(3,4),(4,3)。
/*
1. 二分k
2.从所有1出发 bfs k层
3.正方形 包含剩下的所有点
*/
#include <iostream>
#include <algorithm>
#include <limits.h>
#include <cstring>
#include <queue>
using namespace std;
const int N = 255;
int n, m;
string g[N]; //地图
int dist[N][N]; //每个点到达补给站的距离
void bfs(int k)
{
queue<pair<int, int>> q; //宽搜队列
memset(dist, -1, sizeof dist);
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++)
{
if(g[i][j] == '1')
{
dist[i][j] = 0;
q.push({i, j});
}
}
}
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
while(q.size())
{
auto t = q.front();
q.pop();
int x = t.first, y = t.second;
int distance = dist[x][y];
if(distance == k) continue;
for(int i = 0; i < 4; i ++)
{
int a = x + dx[i], b = y + dy[i];
if(a >= 0 && a < n && b >= 0 && b < m && dist[a][b] == -1)
{
dist[a][b] = distance + 1;
q.push({a, b});
}
}
}
}
bool check(int k)
{
bfs(k);
int min_sum = INT_MAX, max_sum = INT_MIN;
int min_sub = INT_MAX, max_sub = INT_MIN;
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++)
if(dist[i][j] == -1)
{
min_sum = min(min_sum, i + j);
max_sum = max(max_sum, i + j);
min_sub = min(min_sub, i - j);
max_sub = max(max_sub, i - j);
}
if(min_sum == INT_MAX) return true; //所有点都可以访问到
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j++)
if(g[i][j] == '0') //当前没有补给站,枚举补给站
{
int sum = max(abs(i + j - min_sum), abs(i + j - max_sum));
int sub = max(abs(i - j - min_sub), abs(i - j - max_sub));
if(max(sum, sub) <= k) return true;
}
return false;
}
int main()
{
int T;
cin >> T;
for(int C = 1; C <= T; C ++)
{
cin >> n >> m;
for(int i = 0; i < n; i ++) cin >> g[i];
//二分
int l = 0, r = n + m;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("Case #%d: %d\n", C, r);
}
return 0;
}