第2题-足球队
You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.
题面描述:
塔子哥的足球队有n个队员,参加了m次点球射门,每次射中用1表示,射失用0表示。为了评估队员的射门能力,塔子哥制定了四个排序标准:首先比较进球总数,其次比较最多一次连续进球的个数,再者比较第一次射失的顺序,最后若以上都相同,则按队员编号从小到大排序。输入包括两个整数n和m,以及每个队员的射门结果,输出队员编号按能力强弱排序。
思路
自定义排序实现。
本质就是一个自定义的实现。
-
第一关键字是每个字符串中 1 的数量,从大到小
-
第二关键字是每个字符串中最长的连续 1 的数量,从大到小
-
第三关键字:
- 此时两个比较的串 A 和 B 中 0 的索引数量必然相同,假设数量为 k
- 依次比较两个串中的第一个为 0 的索引,第二个为 0 的索引,... ,第 k 个为 0 的索引
- 如果对于串 A 和串 B ,前 i 个索引均一一对应相同,第 i+1 个索引满足 indexA[i] != indexB[i]
- 如果 indexA[i] > indexB[i] ,则 A 串为 0 的这个索引更靠后,则 A 串排序后更靠前
- 如果 indexA[i] < indexB[i] ,则 B 串为 0 的这个索引更靠后,则 B 串排序后更靠前
- 如果 indexA[i] = indexB[i] ,则继续比较之后的索引,如果 A 和 B 所有为 0 的索引均相同,则比较 A 和 B 的编号,即第四关键字
-
第四关键字是每个字符串的编号,从小到大
需要预处理出前两个关键字,第三关键字的比较需要至多 O(m) 次,最终都相同则按编号从小到大即可。
时间复杂度:O(nmlogn)
具体步骤
本题的核心是根据给定的点球射门结果,对队员的射门能力进行自定义排序。我们需要定义几个关键字来实现这一排序,具体步骤如下:
-
定义数据结构: 我们定义一个结构体
StringInfo来存储每个队员的射门信息,主要包含以下内容:index: 队员的原始编号one_count: 队员进球的总数(字符串中1的数量)max_con: 队员最多连续进球的次数(最长连续1的长度)zeros: 队员每次射失的索引位置(字符串中0的位置列表)
-
输入读取: 首先,读取队员的数量
n和训练次数m,接着逐个读取每个队员的射门结果字符串,并计算各项指标。 -
计算指标: 对于每个队员的射门结果字符串:
- 统计其中
1的数量,得到one_count。 - 遍历字符串以计算
max_con,即最长的连续1的长度。 - 同时记录每个
0的位置,以便后续的比较。
- 统计其中
-
自定义排序: 使用 C++ 的
sort函数对StringInfo结构体的数组进行排序,排序的优先级依次为:- 按照
one_count从大到小。 - 若
one_count相同,则按照max_con从大到小。 - 若
max_con仍然相同,比较zeros列表中的索引,依次判断哪个队员在早期射失。 - 若
zeros也相同,则根据队员的原始编号升序排序。
- 按照
-
输出结果: 最后,将排序后的队员编号按顺序输出。
代码
Java
import java.util.*;
class StringInfo implements Comparable<StringInfo> {
int index; // 字符串的原始索引
int oneCount; // '1'的数量
int maxCon; // 最大连续'1'的长度
List<Integer> zeros; // '0'的位置列表
// 构造函数
public StringInfo() {
zeros = new ArrayList<>();
}
// 自定义排序规则
@Override
public int compareTo(StringInfo other) {
if (this.oneCount != other.oneCount)
return Integer.compare(other.oneCount, this.oneCount); // 按照'1'的数量降序排序
if (this.maxCon != other.maxCon)
return Integer.compare(other.maxCon, this.maxCon); // 按照连续'1'的最大长度降序排序
for (int i = 0; i < Math.min(this.zeros.size(), other.zeros.size()); i++) {
if (!this.zeros.get(i).equals(other.zeros.get(i))) {
return Integer.compare(other.zeros.get(i), this.zeros.get(i)); // 按照'0'的位置列表进行排序
}
}
return Integer.compare(this.index, other.index); // 最后按照原始索引升序排序
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 读取n的值
int m = scanner.nextInt(); // 读取m的值
List<StringInfo> infos = new ArrayList<>(n); // 存储每个字符串的信息
for (int i = 0; i < n; ++i) {
String s = scanner.next(); // 读取每个字符串
StringInfo info = new StringInfo();
info.index = i; // 记录字符串的原始索引
info.oneCount = 0;
info.maxCon = 0;
// 计算'1'的数量和'0'的位置
for (int j = 0; j < m; ++j) {
if (s.charAt(j) == '1') {
info.oneCount++;
} else {
info.zeros.add(j);
}
}
// 计算连续'1'的最大长度
int j = 0;
while (j < m) {
if (s.charAt(j) == '0') {
j++;
continue; // 跳过'0'
}
int k = j + 1;
while (k < m && s.charAt(k) == '1') {
k++;
}
info.maxCon = Math.max(info.maxCon, k - j); // 更新最大长度
j = k;
}
infos.add(info); // 将计算结果保存到数组中
}
// 对所有字符串信息进行排序
Collections.sort(infos);
// 输出排序后的结果
for (StringInfo info : infos) {
System.out.print((info.index + 1) + " ");
}
System.out.println();
}
}
C++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 定义一个结构体来封装每个字符串的相关信息
struct StringInfo {
int index; // 字符串的原始索引
int one_count; // '1'的数量
int max_con; // 最大连续'1'的长度
vector<int> zeros; // '0'的位置列表
// 自定义排序规则
bool operator<(const StringInfo& other) const {
if (one_count != other.one_count)
return one_count > other.one_count; // 按照'1'的数量降序排序
if (max_con != other.max_con)
return max_con > other.max_con; // 按照连续'1'的最大长度降序排序
if (zeros != other.zeros) {
for(int i=0;i<zeros.size();i++){
if(zeros[i] < other.zeros[i]){
return false;
}else if(zeros[i] > other.zeros[i]){
return true;
}
}
}
return index < other.index; // 最后按照原始索引升序排序
}
};
int main() {
int n, m;
cin >> n >> m; // 读取n和m的值
vector<StringInfo> infos(n); // 存储每个字符串的信息
for (int i = 0; i < n; ++i) {
string s;
cin >> s; // 读取每个字符串
StringInfo info;
info.index = i; // 记录字符串的原始索引
info.one_count = 0;
info.max_con = 0;
// 计算'1'的数量和'0'的位置
for (int j = 0; j < m; ++j) {
if (s[j] == '1') {
info.one_count++;
} else {
info.zeros.push_back(j);
}
}
// 计算连续'1'的最大长度
int j = 0;
while (j < m) {
if (s[j] == '0') {
j++;
continue; // 跳过'0'
}
int k = j + 1;
while (k < m && s[k] == '1') {
k++;
}
info.max_con = max(info.max_con, k - j); // 更新最大长度
j = k;
}
infos[i] = info; // 将计算结果保存到数组中
}
// 对所有字符串信息进行排序
sort(infos.begin(), infos.end());
// 输出排序后的结果
for (const auto& info : infos) {
cout << info.index + 1 << " ";
}
cout << endl;
return 0;
}
Python
n, m = map(int, input().split())
lst = input().split()
one = [0] * n
con = [0] * n
zero = [[] for _ in range(n)]
for i in range(n):
s = lst[i]
for j in range(m):
if s[j] == '1':
one[i] += 1
else:
zero[i].append(j)
j = 0
while j < m:
if s[j] == '0':
j += 1
continue
k = j + 1
while k < m and s[k] == '1':
k += 1
con[i] = max(con[i], k - j)
j = k
a = list(range(n))
a.sort(key=lambda x: (-one[x], -con[x], [-z for z in zero[x]], x))
print(*[x + 1 for x in a])
javaScript
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
let [n, m] = (await readline()).split(" ").map(Number); // 读取 n 和 m
let lst = (await readline()).split(" "); // 读取字符串列表
let infos = []; // 存储每个字符串的信息
for (let i = 0; i < n; i++) {
let s = lst[i];
let oneCount = 0;
let maxCon = 0;
let zeros = []; // 存储 '0' 的索引
// 计算 '1' 的数量和 '0' 的索引
for (let j = 0; j < m; j++) {
if (s[j] === '1') {
oneCount++;
} else {
zeros.push(j);
}
}
// 计算最长连续 '1' 的长度
let j = 0;
while (j < m) {
if (s[j] === '0') {
j++;
continue;
}
let k = j + 1;
while (k < m && s[k] === '1') {
k++;
}
maxCon = Math.max(maxCon, k - j);
j = k;
}
infos.push({ index: i, oneCount, maxCon, zeros });
}
// 排序
infos.sort((a, b) => {
if (a.oneCount !== b.oneCount) return b.oneCount - a.oneCount; // '1' 的数量降序
if (a.maxCon !== b.maxCon) return b.maxCon - a.maxCon; // 最长连续 '1' 的降序
if (a.zeros.length !== b.zeros.length) return b.zeros.length - a.zeros.length; // '0' 数量多的排前
for (let i = 0; i < a.zeros.length; i++) {
if (a.zeros[i] !== b.zeros[i]) return b.zeros[i] - a.zeros[i]; // '0' 位置降序
}
return a.index - b.index; // 原始索引升序
});
console.log(infos.map(info => info.index + 1).join(" "));
rl.close();
})();
题目描述
话说小明带ACM队已经征战沙场多年,又又又又是时候拓展业务面了,你猜怎么着,当上足球领队了,现在正在进行紧张的备赛,冲击篮球杯金牌。
小明的足球队一共有n个队员,小明安排他们参与m次点球射门,每次射中用1表示,射失(没射中)用0表示,小明为他的球员能力强弱拟定了如下标准:
(1)进球总数更多的队员射门能力更强;
(2)若进球总数一样多,则比较最多一次连续进的个数,更多的队员能力更强;
(3)若最多一次连续进球个数一样多,则比较第一次射失的先后顺序,其中后射失的队员更强,若第一次射失顺序相同,则按继续比较第二次射失的顺序,后丢球的队员能力更强,依次类推;
(4)若前3个规则排序后还能力相等,则队员编号更小的能力更强。
输入描述
第一行输入两个整数n和m,分别表示足球队中队员的数量,以及射门训练的次数。(队员编号从1开始,依次递增)2n−1个整数,表示整棵满二叉搜索树。其中1≤n≤10,整数之间用空格分割。
第二行,第1~n个队员从第1到m次训练的进球情况,每个队员进球情况为连续的1和0的组合,不同队员的情况用空格分隔
规定:所有n和m均为整数1≤n,m≤1000。
输出描述
所有球员射门能力从强到弱的队员编号,用空格分隔每个编号
样例一
输入
4 5
11100 00111 10111 01111
输出
4 3 1 2
解释
4个队员,射门训练5次,队员3和4进球数均为4个,比队员1H和2的3个更多,队员3连续进球故最多一次为3个,而队员4最大为4。因此队员4射门能力强于队员3,另外队员2比队员1先丢球,因此队员1射门能力强于队员2,顺序为4 3 1 2。
样例二
输入
2 10
1011100111 1011101101
输出
2 1
解释
2个队员,射门训练10次,两个队员的进球总数均为7个,连续进球最多的均为3个,且第前两次丢球顺序均为第2次和第6次训练射门,而队员2第三次丢球为第9次训练,队员1为第7次训练,因此队员2的射门能力强于队员1,顺序为2 1
Limitation
1s, 1024KiB for each test case.
塔子周赛(二)华为暑期实习-2024年4月24号场
- Status
- Done
- Rule
- IOI
- Problem
- 3
- Start at
- 2025-3-21 19:00
- End at
- 2025-3-21 21:00
- Duration
- 2 hour(s)
- Host
-
TaZi
- Partic.
- 44