#P2328. 第2题-足球队
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 956
            Accepted: 271
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2024年4月24日-暑期实习
                              
                      
          
 
- 
                        算法标签>排序算法          
 
第2题-足球队
题面描述:
塔子哥的足球队有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.
Related
In following contests: