#P2654. 魔法链表
-
1000ms
Tried: 161
Accepted: 38
Difficulty: 5
所属公司 :
华为
时间 :2025年2月19日-留学生
-
算法标签>模拟
魔法链表
题解
题面描述
给定一个正整数 m和 m 条链表,每条链表由若干数字构成,并且同一条链表中任一数字 n 都满足计算公式 key=(2×n+1)modm 得到的关键字均相同。这 m 条链表初始时已按“链表长度从小到大排序(若长度相同则关键字小的排前)”排列。
随后,给出两个数字 x 和 y ,要求对所有“尾数”为 x(即数字的个位数字等于x的元素执行更新操作:将该元素加上 y 。更新后,重新计算其关键字: new_key=(2×(n+y)+1)modm 若新关键字与原链表的关键字相同,则直接在原位置更新数字;否则,从原链表中删除该元素,并将更新后的数字插入到新链表的尾部。插入后依然要求每个链表中所有数字关键字相同。最后,所有链表按照“元素个数从小到大排列(若个数相同则关键字小的排前)”输出,每条链表一行。
解题思路
-
存储与标识链表
题目给出的 m 行数字,每一行的第一个数字可以计算出该链表对应的关键字,故可将每条链表存为一个结构体,包含字段:key:该链表的关键字。data:存放链表中各个数字的数组(或 vector)。
-
更新操作
遍历每条链表中的所有元素:- 对于每个数字 n,判断其个位数字是否等于 x(即 n)。
- 若不满足条件,则不变;若满足,则令 nnew=n+y ,再计算新关键字:
newkey=(2∗nnew+1)modm
- 如果新关键字与当前链表的
key相同,则直接将数字更新为 nnew(在原位置更新,不改变顺序)。 - 如果新关键字不同,则将该元素从当前链表中删除,并追加到目标链表(关键字为 new_key)的尾部。注意追加的顺序需要保持“插入到链表末尾”的要求。
- 链表重新排序
更新完成后,由于各链表的元素个数可能发生变化,需要将所有链表按照“元素个数从小到大排列;若个数相同,则关键字较小的排前”进行排序,最后按行输出每个链表中的数字。
cpp
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 定义结构体表示一条链表,包含关键字和数据数组
struct Chain {
int key; // 链表的关键字
vector<int> data; // 存储链表中的所有数字
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m;
cin >> m;
cin.ignore(); // 忽略行末换行符
// 用一个 vector 保存 m 条链表
vector<Chain> chains;
chains.reserve(m);
// 为了方便查找目标链表,将 key 和链表的对应关系记录下来
// 先读取 m 行链表数据
for (int i = 0; i < m; i++) {
string line;
getline(cin, line);
if(line.empty()) { i--; continue; } // 处理可能的空行
// 使用 stringstream 分割每行数字
stringstream ss(line);
vector<int> nums;
int num;
while(ss >> num){
nums.push_back(num);
}
// 根据第一个数字计算链表关键字
int key = (2 * nums[0] + 1) % m;
chains.push_back({key, nums});
}
// 读取最后一行 x 和 y
int x, y;
cin >> x >> y;
// 创建一个临时容器记录目标链表待追加的数字
// 下标对应关键字
vector<vector<int>> appended(m);
// 对每个链表进行遍历更新
for (auto &chain : chains) {
vector<int> newData; // 存放更新后仍留在本链表的元素
// 遍历当前链表中的每个数字
for (int n : chain.data) {
// 判断是否满足尾数为 x(即个位数字)
if(n % 10 == x) {
int updated = n + y; // 更新后的值
int newKey = (2 * updated + 1) % m; // 更新后的关键字
// 若更新后关键字不变,则在本链表更新
if(newKey == chain.key){
newData.push_back(updated);
} else {
// 否则,将该数字从本链表删除,并追加到目标链表末尾
appended[newKey].push_back(updated);
}
} else {
// 不满足条件的数字原样保留
newData.push_back(n);
}
}
// 更新本链表数据
chain.data = newData;
}
// 将每个链表待追加的数字追加到链表末尾
for (auto &chain : chains) {
chain.data.insert(chain.data.end(), appended[chain.key].begin(), appended[chain.key].end());
}
// 对所有链表按照“元素个数从小到大排序;若个数相同则关键字较小的排前”排序
sort(chains.begin(), chains.end(), [](const Chain &a, const Chain &b) {
if(a.data.size() == b.data.size())
return a.key < b.key;
return a.data.size() < b.data.size();
});
// 输出所有链表,每条链表中的数字以空格分隔
for (auto &chain : chains) {
for (int i = 0; i < chain.data.size(); i++){
cout << chain.data[i];
if(i < chain.data.size()-1) cout << " ";
}
cout << "\n";
}
return 0;
}
python
# 读取输入
m = int(input())
chains = [] # 用于存放每条链表,存储形式为字典:{'key': key, 'data': [数字列表]}
# 读取 m 行链表数据
for _ in range(m):
line = input().strip()
# 跳过空行(若有)
if not line:
continue
nums = list(map(int, line.split()))
# 根据第一数字计算关键字 (2*n+1)%m
key = (2 * nums[0] + 1) % m
chains.append({'key': key, 'data': nums})
# 读取最后一行 x 和 y
x_y = input().split()
x, y = int(x_y[0]), int(x_y[1])
# 建立一个字典记录目标链表待追加的元素,key 为链表关键字
appended = {i: [] for i in range(m)}
# 对每个链表进行遍历更新
for chain in chains:
new_data = [] # 存放更新后依然留在本链表的元素
for n in chain['data']:
# 判断数字尾数(个位)是否为 x
if n % 10 == x:
updated = n + y # 更新后的值
new_key = (2 * updated + 1) % m # 更新后的关键字
if new_key == chain['key']:
# 关键字不变,直接更新
new_data.append(updated)
else:
# 否则,从本链表删除,添加到目标链表末尾
appended[new_key].append(updated)
else:
new_data.append(n)
chain['data'] = new_data
# 将待追加的元素追加到各自链表末尾
for chain in chains:
chain['data'].extend(appended[chain['key']])
# 对所有链表排序:先按照链表长度,从小到大;若长度相同则关键字小的排前
chains.sort(key=lambda ch: (len(ch['data']), ch['key']))
# 输出每条链表,数字之间以空格分隔
for chain in chains:
print(" ".join(map(str, chain['data'])))
java
import java.util.*;
import java.io.*;
public class Main {
// 定义一个类表示一条链表
static class Chain {
int key; // 链表关键字 (由 (2*n+1)%m 得到)
ArrayList<Integer> data; // 存放链表中的数字
public Chain(int key, ArrayList<Integer> data) {
this.key = key;
this.data = data;
}
}
public static void main(String[] args) throws IOException {
// 建立输入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int m = Integer.parseInt(br.readLine().trim());
ArrayList<Chain> chains = new ArrayList<>();
// 读取 m 行链表数据
for (int i = 0; i < m; i++) {
String line = br.readLine();
// 若遇到空行,继续读取
while(line.trim().isEmpty()){
line = br.readLine();
}
String[] parts = line.trim().split("\\s+");
ArrayList<Integer> nums = new ArrayList<>();
for (String s : parts) {
nums.add(Integer.parseInt(s));
}
// 根据第一个数字计算关键字
int key = (2 * nums.get(0) + 1) % m;
chains.add(new Chain(key, nums));
}
// 读取最后一行 x 和 y
String[] lastLine = br.readLine().trim().split("\\s+");
int x = Integer.parseInt(lastLine[0]);
int y = Integer.parseInt(lastLine[1]);
// 建立一个 List 数组,用于记录目标链表待追加的元素,下标对应链表关键字
ArrayList<Integer>[] appended = new ArrayList[m];
for (int i = 0; i < m; i++) {
appended[i] = new ArrayList<>();
}
// 遍历每个链表,进行更新操作
for (Chain chain : chains) {
ArrayList<Integer> newData = new ArrayList<>();
for (int n : chain.data) {
// 判断数字的个位是否等于 x
if(n % 10 == x) {
int updated = n + y; // 更新后的值
int newKey = (2 * updated + 1) % m; // 更新后的关键字
if(newKey == chain.key){
// 关键字未变,直接更新
newData.add(updated);
} else {
// 否则,从本链表中删除,添加到目标链表末尾
appended[newKey].add(updated);
}
} else {
newData.add(n);
}
}
chain.data = newData;
}
// 将各链表待追加的元素追加到链表末尾
for (Chain chain : chains) {
chain.data.addAll(appended[chain.key]);
}
// 对所有链表排序:先按照元素个数,从小到大;若个数相同则关键字较小的排前
Collections.sort(chains, new Comparator<Chain>() {
public int compare(Chain a, Chain b) {
if(a.data.size() == b.data.size()){
return a.key - b.key;
}
return a.data.size() - b.data.size();
}
});
// 输出结果,每条链表占一行,数字之间空格分隔
StringBuilder sb = new StringBuilder();
for (Chain chain : chains) {
for (int i = 0; i < chain.data.size(); i++) {
sb.append(chain.data.get(i));
if(i < chain.data.size()-1) {
sb.append(" ");
}
}
sb.append("\n");
}
System.out.print(sb.toString());
}
}
题目内容
给定一个数字 m ,将一堆数字中的每一个按值(假定值为 n )进行运算分成 m 类,运算公式为 (2∗n+1)%m ,算出结果值为关键字。相同关键字的数字组成一个链表,所以共有 m 条链表,每条链表中的值占据一行,根据 m 条链表的元素个数从小到大顺序排列(个数相同时,关键字小的排在前面),先假定对于一个给定满足上述条件的数字已按规则排列完全后,在此基础上做一项操作,即把所有尾数是 x 的值 n 加上一个值 y ,调整链表组成,插入的元素要求放在链表的末尾,使得变化后的链表依然满足上述条件,具体满足如下两点:
(1) 同一个链表中的元素值关健字一样
(2) 链表间的排列顺序:按元素个数从小到大排列(个数相同时,关键字小的排在前面)
调整链表组成时,如果让放到原链表,则只更新值即可,而且不需要调整顺序(即:不需要移动到链表末尾)。只有调整到其他链表时才需要。
输入描述
输入第一行是数字 m ,接下来的 m 行数字,用 n 表示每一行的任一个数字,那么这一行按 (2∗n+1) 计算结果都一样,且已经按长度排序好,最后一行由两个数组组成 x y,表示对尾数是 x 的元素值增加 y ,0<m≤10,0<n<100,0<x,y≤100
输出描述
满足上述条件调整后的 m 个链表
样例1
输入
3
7 13
11 17 8
12 9 27 15
7 30
输出
37 13
11 47 8
12 9 57 15
说明
m 值为 3,3 条链表值已分类好,将尾数为 7 的值加 30 后,发生如下变化:
7−>37,17−>47,27−>57 关键字均不变,只需要修改原先的元素值
样例2
输入
3
7 13
11 17 8
12 9 27 15
5 2
输出
7 13
12 9 27
11 17 8 17
说明
m 值为 3,3 条链表值已分类好,将尾数为 5 的值加 2 后,发生如下变化:15−>17 ,关键字由 1 变为 2 ,15 从原来位置删除,17 插入新位置,同时链表长度变化,需要变化输出顺序