趁现在还....

趁阳光正好,趁微风不噪,趁繁花还未开至荼靡,趁现在还年轻可以走很长的路,还能诉说很深很深的思念,趁世界还不那么拥挤,趁飞机现在还没起飞,趁现在时光还没吞噬所有的思念,趁现在自己的双手还能拥抱彼此,趁我们现在还能呼吸,去做我们想做的事吧,去见我们想见的人吧,去说我们想说的话吧

<think> 题目要求:对于给定的t组测试数据,每组数据是一个n行m列的矩阵,表示队伍。每一列代表一个班级。我们需要对每个班级(每一列)按照身高从低到高排序(从小到大),但是注意:值为0的位置表示没有人,排序时0应该被视为一个特殊值,即0不用参与排序,但是0的位置在排序后仍然应该保持在原来的位置(即排序只对非0的元素进行,并且非0元素排序后,0的位置不变)。 思路: 对于每一列,我们单独处理: 1. 提取该列中所有非0的元素,并进行排序(从小到大)。 2. 然后,我们再次遍历该列,将非0的元素按顺序替换成排序后的结果,而0的位置保持不变。 注意:题目中说明了每个班级至少有一名学生,所以每一列至少有一个非0元素(不会全为0)。 由于数据规模:n*m的总和不超过3e6,所以我们可以对每一列单独提取非0元素,排序,然后重新放回去。这样每列的时间复杂度是O(n log n),总时间复杂度为O(m * n log n),而m和n最大都是1000,那么最坏情况下m=1000,n=1000,则总操作次数为1000 * (1000 log1000) ≈ 1000 * 1000 * 10 = 10e6,再加上总数据量n*m不超过3e6(注意题目说的是n×m的总和不超过3e6,也就是所有测试数据的总矩阵大小不超过3e6),所以这样处理是可行的。 具体步骤: 1. 读入t。 2. 对于每组数据: 读入n, m。 读入n行,每行m个整数。 创建一个新的矩阵(或者直接在原矩阵上修改),我们按列处理。 对于每一列j(0<=j<m): 创建一个临时数组temp,用于存放该列的非0元素。 遍历该列的所有行(i从0到n-1),如果当前元素不为0,则将其加入temp。 对temp进行排序(从小到大)。 然后再次遍历该列,对于该列的每一个位置,如果原位置不是0,则用排序后的下一个元素替换;如果是0,则保持不变。 例如样例中的第一组数据: 输入: 3 3 132 136 133 0 132 137 0 163 168 处理第一列:非0元素有[132],排序后还是[132];然后重新放回,第一列变为:132, 0, 0 -> 注意,0的位置不变。 处理第二列:非0元素有[136,132,163] -> 排序后:[132,136,163];然后重新放回:第一个元素132(替换原来的136)、第二个元素132(替换原来的132)?不对,这里要注意:我们重新放回的时候,应该按照原列的顺序,遇到非0的位置就放入排序后的元素,遇到0就跳过。 原第二列:[136,132,163] -> 非0元素提取出来为[136,132,163],排序后为[132,136,163]。 然后重新放回:第一行第二列原为136,现在改为132;第二行第二列原为132,现在改为136;第三行第二列原为163,现在改为163。所以第二列变为:132,136,163。 处理第三列:非0元素[133,137,168] -> 排序后[133,137,168],然后放回:第一行第三列133->133,第二行137->137,第三行168->168。 输出: 132 132 133 0 136 137 0 163 168 但是注意样例输出第一组是: 132 132 133 0 136 137 0 163 168 所以第二列的处理:原第二列是[136,132,163] -> 排序后的非0序列是[132,136,163]。 重新放回时: 第一行第二列:136 -> 替换为132(排序后序列的第一个) 第二行第二列:132 -> 替换为136(第二个) 第三行第二列:163 -> 替换为163(第三个) 因此,我们需要一个指针k,从0开始,指向排序后数组的当前元素。遍历该列每一行,如果原位置不是0,我们就将排序后的数组的第k个元素放在这里,然后k++;如果是0,则跳过(不改变,也不移动k)。 代码实现: 注意:题目要求输出时每个位置用空格分隔,每行末尾没有多余空格。 另外,样例第二组数据: 输入: 3 4 146 134 125 163 168 153 162 153 156 121 143 0 输出: 146 121 125 153 156 134 143 163 168 153 162 0 我们检查第二列(索引1): 原第二列:[134, 153, 121] -> 提取非0:[134,153,121] -> 排序:[121,134,153] 重新放回: 第一行第二列:134 -> 121 第二行第二列:153 -> 134 第三行第二列:121 -> 153 所以第二列变为:[121,134,153] -> 符合输出。 第三列(索引2): 原第三列:[125,162,143] -> 非0排序:[125,143,162] 重新放回: 第一行:125 -> 125 第二行:162 -> 143? 不对,输出第二行第三列是143吗?实际输出是: 第一行:125 第二行:143 第三行:162 但是原第三列第三行是143,输出第三行第三列是162(因为第三行第三列是143,重新放回时,第一行第三列:125(原125)->125;第二行第三列:162(原162)->143(这里错了?) 不对,应该是: 重新放回的过程:遍历该列每一行: 第一行:125 -> 非0,所以用排序后的下一个(第一个)125 -> 这里变成125(不变),k=1 第二行:162 -> 非0,用排序后的第二个元素143 -> 这里变成143,k=2 第三行:143 -> 非0,用排序后的第三个元素162 -> 这里变成162,k=3 所以第三列变为:125,143,162 -> 但是输出中第三列是:125,143,162,而输出样例第三列是:125,143,162(第一行125,第二行143,第三行162)?但是我们看样例输出第二行第三列是143,第三行第三列是162,所以是对的。 但是注意输出样例第二组: 146 121 125 153 156 134 143 163 168 153 162 0 第三列:第一行125,第二行143,第三行162 -> 符合。 第四列: 原第四列:[163,153,0] -> 非0元素[163,153] -> 排序后[153,163] 重新放回: 第一行:163 -> 153 第二行:153 -> 163 第三行:0 -> 不变(0) 所以第四列:153,163,0 -> 符合输出。 因此,我们按列处理即可。 代码步骤: for (int group = 0; group < t; group++) { 读入n, m 初始化二维数组 grid[n][m] 读入n行数据 创建一个二维数组 result(或者直接修改grid,但注意放回的时候不能影响后续列的读取,因为我们是按列独立处理的,所以可以原地修改?但是注意,当我们处理某一列时,我们只读取该列原来的数据,然后修改该列。所以我们可以直接修改grid,因为处理每一列时,我们提取非0元素是从当前列中提取(即原数据),然后修改该列。所以不会影响其他列。 但是注意:如果我们按列处理,那么处理第一列时,我们修改了第一列的值,那么在处理第二列时,读取第二列的值会不会被第一列修改影响?不会,因为第二列的值并没有被修改(我们在处理第二列时,读取的是grid[i][j]中的j是第二列,而第一列已经被修改了,但是第二列还没有被修改,所以我们读取的是原来的第二列)。所以可以按列原地修改。 然而,我们重新放回的时候,会覆盖原来的值,但是我们在处理某一列时,已经将该列的所有原始数据提取出来了(在排序前已经提取),所以放回时修改该列不会影响其他列的处理。 所以,我们可以这样: for (int j = 0; j < m; j++) { vector<int> nonZero; // 首先收集该列的非0元素 for (int i = 0; i < n; i++) { if (grid[i][j] != 0) { nonZero.push_back(grid[i][j]); } } // 排序 sort(nonZero.begin(), nonZero.end()); // 然后重新放回该列 int idx = 0; // 指向nonZero的下标 for (int i = 0; i < n; i++) { if (grid[i][j] != 0) { grid[i][j] = nonZero[idx++]; } // 否则,0保持不变,不进行任何操作 } } 最后输出grid。 } 输出格式:每行m个数,每个数用空格隔开,行末无空格。 注意:题目要求输出时,每个整数后面跟一个空格,但是行末没有空格?样例输出每行末尾是有空格的?我们看样例输出: 132 132 133 0 136 137 0 163 168 实际上,样例输出每行末尾都有一个空格。但是题目没有明确要求,我们按照样例输出格式:每个数字后面跟一个空格,包括最后一个数字后面也有空格,然后换行。 但是,如果我们这样输出: for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cout << grid[i][j]; if (j < m-1) cout << ' '; } cout << endl; } 这样输出的每行末尾没有空格,但是样例输出每行末尾有空格。 观察样例输出:每行最后一个数字后面也有一个空格,然后换行。所以我们可以这样: for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cout << grid[i][j] << ' '; } cout << endl; } 但是这样每行末尾会多一个空格,题目是否接受?我们可以尝试,或者看样例输出要求。 但是题目描述输出格式是:“输出 一个 n 行 m 列的方阵”,没有具体说明空格,但是样例输出每行末尾都有空格。 为了保险,我们可以按照样例输出格式:每个数字后面跟一个空格,包括最后一个数字后面也有空格,然后换行。这样输出。 但是,在编程竞赛中,通常行末空格是允许的,只要格式一致即可。 我们按照样例输出格式,即每个数字后面都输出一个空格,然后换行。 但是注意:题目中有一组数据是0,我们输出0即可。 代码实现(C++): </think> 为了解决这个问题,我们需要对给定的矩阵进行按列排序,其中每一列代表一个班级。排序要求是每个班级(列)的非零元素按照身高从小到大排序,而值为0的位置保持不变。以下是详细的解决方案和代码实现: ### 解决方案 1. **读取输入数据**:首先读取测试数据的组数 `t`,然后对于每组数据,读取矩阵的行数 `n` 和列数 `m`,接着读取矩阵的具体数据。 2. **处理每一列**:对于每一列,执行以下操作: - **提取非零元素**:遍历该列的所有行,收集所有非零元素到一个临时数组中。 - **排序非零元素**:对临时数组中的非零元素进行升序排序。 - **重新填充列**:再次遍历该列,将排序后的非零元素依次放回原位置(跳过值为0的位置)。 3. **输出结果**:处理完所有列后,输出排序后的矩阵,每个元素后面跟一个空格,每行末尾换行。 ### 代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector<vector<int>> grid(n, vector<int>(m)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> grid[i][j]; } } // 处理每一列 for (int j = 0; j < m; j++) { vector<int> nonZero; // 收集非零元素 for (int i = 0; i < n; i++) { if (grid[i][j] != 0) { nonZero.push_back(grid[i][j]); } } // 排序非零元素 sort(nonZero.begin(), nonZero.end()); // 重新填充列 int idx = 0; for (int i = 0; i < n; i++) { if (grid[i][j] != 0) { grid[i][j] = nonZero[idx++]; } } } // 输出结果 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cout << grid[i][j] << ' '; } cout << '\n'; } } return 0; } ``` ### 代码解释 1. **输入处理**: - 使用 `ios::sync_with_stdio(false)` 和 `cin.tie(nullptr)` 加速输入输出。 - 读取测试数据组数 `t`,然后依次处理每组数据。 - 读取矩阵的行数 `n` 和列数 `m`,并初始化一个 `n × m` 的矩阵 `grid`。 2. **按列排序**: - 对于每一列 `j`,创建一个临时数组 `nonZero` 存储该列所有非零元素。 - 对 `nonZero` 进行升序排序。 - 再次遍历该列,将排序后的非零元素依次放回原位置(跳过值为0的位置)。 3. **输出结果**: - 遍历排序后的矩阵,每个元素后面输出一个空格,每行末尾换行。 ### 复杂度分析 - **时间复杂度**:对于每组数据,处理每列的时间复杂度为 O(n log n),其中 `n` 是行数。总时间复杂度为 O(t × m × n log n)。由于所有测试数据的 `n × m` 总和不超过 3×10⁶,因此该算法是高效的。 - **空间复杂度**:临时数组 `nonZero` 的空间复杂度为 O(n),总体空间复杂度为 O(n × m)。 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值