拍集体照时队形很重要,这里对给定的 N 个人 K 排的队形设计排队规则如下:
-
每排人数为 /(向下取整),多出来的人全部站在最后一排;
-
后排所有人的个子都不比前排任何人矮;
-
每排中最高者站中间(中间位置为 /,其中 m 为该排人数,除法向下取整);
-
每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧(例如5人身高为190、188、186、175、170,则队形为175、188、190、186、170。这里假设你面对拍照者,所以你的左边是中间人的右边);
-
若多人身高相同,则按名字的字典序升序排列。这里保证无重名。
现给定一组拍照人,请编写程序输出他们的队形。
输入格式:
每个输入包含 1 个测试用例。每个测试用例第 1 行给出两个正整数 N(≤,总人数)和 K(≤,总排数)。随后 N 行,每行给出一个人的名字(不包含空格、长度不超过 8 个英文字母)和身高([30, 300] 区间内的整数)。
输出格式:
输出拍照的队形。即K排人名,其间以空格分隔,行末不得有多余空格。注意:假设你面对拍照者,后排的人输出在上方,前排输出在下方。
输入样例:
10 3
Tom 188
Mike 170
Eva 168
Tim 160
Joe 190
Ann 168
Bob 175
Nick 186
Amy 160
John 159
输出样例:
Bob Tom Joe Nick
Ann Mike Eva
Tim Amy John
分析:
第三排:Bob Tom Joe Nick
第二排:Ann Mike Eva
第一排:Tim Amy John
单独看第三排的人的身高
Bob Tom Joe Nick
175 188 190 186
假定最开始他们是有序的
即:190 188 186 175
题目要求的是,最高的在中间,按身高非增序,先右后左交替入队站在中间人的两侧(这里的左右,是相对于中间人的左右,即。我们面对着中间人,对我们来说就是先左后右)
什么意思呢?
首先,我们先把最高的放中间,190
接着,先左后右的入队,下一个 188,变成 188 190
然后,186 入队, 变成 188 190 186
以此类推可以得到:175 188 190 186
稍微分析一下可以发现这个身高排名规律:
175 188 190 186
no4 no2 no1 no3
(就类似领奖台的那种,冠亚季军站旁边)
假定条件:
t - 用来记录当前一排的最大值的下标,初始化为0
m - 当前一排的人数
res - 存放当前一排的人的名字
peoples - 所有人的名字、身高(已排序,高到低)
1. 首先:
res[m/2] = 身高最高的; // res[m/2] = 190
2. 遍历 188 186 175,将 188 和 175 放在 190 的左边
int j = 0; for (int i = t + 1; i < m + t; i += 2) { res[j++] = peoples[i].name; }
i += 2 就是隔一个取一个
m + t 就可以理解为是一个范围,每次循环,输出peoples数组 [ t, m + t)的内容(看后面解析)
i = t + 1 此时 t = 0,即从 peoples[1]开始,隔一个取一个(peoples[0]已经放到了中间)
第一行是 m = 4, t = 0, 那么这一排的人在peoples中的下标就是 [ 0 , 3)
res 数组变成了 188 175 190
3. 这时候发现,我们要的是 175 188 190,怎么办呢?reverse翻转
reverse(res.begin(), res.begin() + j);
4. 接着我们来处理 190 的右边,和第二步是一样的做法,需要注意的是,执行完第二部之后,另 j += 1 ,因为此时 j == m/2
for (int i = t + 2; i < m + t; i += 2) { res[j++] = peoples[i].name; }
这种情况就不用 reverse 了,因为是按高到低的顺序放到右边的
5. 输出当前一排(没啥好说的了,注意格式
6. 更新 row 和 t 的值
row--;
t = m + t
理解一下 t = m + t
怎么理解 t = m + t
- 当 m = 4, t = 0 时:输出的是 peoples[0] ~ peoples[3] ,更新 t = m + t = 4 + 0 = 4;
- 当 m = 3, t = 4 时:输出的是 peoples[4] ~ peoples[6] ,更新 t = m + t = 3 + 4 = 7;
- 当 m = 3, t = 7 时:输出的是 peoples[7] ~ peoples[9] .....
C++实现:
#include <iostream> #include <string> #include<algorithm> #include <stack> #include <queue> #include <unordered_map> #include <iomanip> using namespace std; /* 190、188、186、175、170 -> 175、188、190、186、170 -> no4、no2、no1、no3、no5
*/ typedef struct Person { string name; int height; }Person; bool cmp(Person a, Person b) { if (a.height != b.height) { return a.height > b.height; } else { return a.name < b.name; } } int main() { int K; // 总排数 int N; // 总人数 cin >> N >> K; vector<Person> peoples(N); for (int i = 0; i < N; ++i) { cin >> peoples[i].name >> peoples[i].height; } sort(peoples.begin(), peoples.end(), cmp); int t = 0; int row = K; // 当前是第几排 int m = 0; // 当前一排的人数 while (row) { if (row == K) { m = N - (K - 1)* N / K; // N÷K是每排的人数 } else { m = N / K; } // 数组都排好序了 // 190, 188, 186, 175, 170, 168, 168, 160, 160, 159 // N = 10, K = 3 // row = 3 // m = N - (K - 1)* N / K // = 10 - (3 - 1) * 10 / 3 // = 10 - 2 * 3 // = 4 最后一排有4个人 vector<string> res(m); //保存目前一排的人的名字 // t = 0, peoples[0]是最大的190,把它放到中间,也就是m/2 res[m / 2] = peoples[t].name; // 接着把188,175放到res[m/2]的左边(保持顺序175 188 res[m/2]),即:隔一个就取一个放到左边 int j = 0; for (int i = t + 1; i < m + t; i += 2) { res[j++] = peoples[i].name; } reverse(res.begin(), res.begin() + j); // 把186,放到res[m/2]的右边(保持顺序),即:隔一个就取一个放到右边 j += 1; for (int i = t + 2; i < m + t; i += 2) { res[j++] = peoples[i].name; } // 输出 cout << res[0]; for (int i = 1; i < m; ++i) { cout << ' ' << res[i]; } cout << endl; t = t + m; row--; } return 0; }
Java实现: