#P2280. 第2题-统计最少媒体包发送源个数
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 308
            Accepted: 95
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2024年10月9日-留学生
                              
                      
          
 
- 
                        算法标签>贪心算法          
 
第2题-统计最少媒体包发送源个数
题目大意
给定一个长度为 n 的序列,问如何将该序列归类为最少的连续递增序列。连续递增序列满足:对于序列中的任意 i,满足 a[i] = a[i-1] + 1。
注意原序列不能被任意打乱
题解:贪心 + 哈希表
思路分析:
题目要求我们计算最少的发送源个数,基于题目的描述,发送源的数据包序列号是连续递增的。如果数据包序列号出现不连续或重复的情况,那么这些数据包必然来自于多个发送源。我们的目标是通过扫描一遍输入序列号,计算出最少的发送源个数。
我们可以使用贪心算法和哈希表来高效解决这个问题。
具体思路:
我们维护一个哈希表 cnt,cnt[x] 代表当前有多少个以序列号 x 结尾的递增子序列。然后逐个处理输入序列号 a[i]。
对于每个序列号 x,有以下两种情况:
- 
x 前面没有以
x-1结尾的序列:
如果哈希表中cnt[x-1]的值为 0,说明当前序列号x需要作为新的递增子序列的开头,因此我们需要新开一个发送源,ans加 1。 - 
x 前面有以
x-1结尾的序列:
如果cnt[x-1]的值大于 0,说明有一个子序列是以x-1结尾的,我们可以将x加入这个子序列,这样并不需要增加新的发送源。同时我们更新哈希表,将cnt[x-1]减 1,表示该子序列不再以x-1结尾,而是改为以x结尾,所以cnt[x]需要加 1。 
通过以上两步的操作,我们可以在一次遍历中动态地计算出最少的发送源个数。
时间复杂度分析:
- 遍历数组的时间复杂度为 O(n),其中 n 是数组的长度。
 - 使用哈希表 
cnt进行查询和更新的时间复杂度为均摊 O(1)。 
因此整体时间复杂度为 O(n),可以在输入数据范围较大的情况下高效处理问题。
代码
Python 实现
from collections import defaultdict
# 输入处理
n = int(input())  # 序列长度
a = list(map(int, input().split()))  # 序列
cnt = defaultdict(int)  # 记录以某个数结尾的递增序列的数量
ans = 0  # 记录最少的连续递增序列的数量
# 遍历序列
for x in a:
    # 如果没有以x-1结尾的递增序列,那么x需要开辟一个新的递增序列
    if cnt[x - 1] == 0:
        ans += 1  # 答案增加一个新的序列
    else:
        # 如果有以x-1结尾的递增序列,那么将x加入该序列
        cnt[x - 1] -= 1  # 以x-1结尾的序列少一个
    cnt[x] += 1  # 更新以x结尾的序列数量
# 输出结果
print(ans)
Java 实现
import java.util.HashMap;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        // 输入处理
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        // 使用HashMap来存储 cnt[x],即以x结尾的递增序列的数量
        HashMap<Integer, Integer> cnt = new HashMap<>();
        int ans = 0; // 记录最少的连续递增序列的数量
        for (int x : a) {
            // 如果没有以x-1结尾的序列,则x需要开辟一个新的序列
            if (!cnt.containsKey(x - 1) || cnt.get(x - 1) == 0) {
                ans++;
            } else {
                // 如果有以x-1结尾的序列,则将x加入该序列
                cnt.put(x - 1, cnt.get(x - 1) - 1);
            }
            // 更新以x结尾的序列数量
            cnt.put(x, cnt.getOrDefault(x, 0) + 1);
        }
        // 输出结果
        System.out.println(ans);
    }
}
C++ 实现
#include <iostream>
#include <unordered_map>
using namespace std;
int main() {
    int n;
    // 输入处理
    cin >> n;
    int a[n];
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    // 使用unordered_map来存储cnt[x],即以x结尾的递增序列的数量
    unordered_map<int, int> cnt;
    int ans = 0; // 记录最少的连续递增序列的数量
    for (int i = 0; i < n; i++) {
        int x = a[i];
        // 如果没有以x-1结尾的序列,则x需要开辟一个新的序列
        if (cnt[x - 1] == 0) {
            ans++;
        } else {
            // 如果有以x-1结尾的序列,则将x加入该序列
            cnt[x - 1]--;
        }
        // 更新以x结尾的序列数量
        cnt[x]++;
    }
    // 输出结果
    cout << ans << endl;
    return 0;
}
        致歉
注意本题的相对顺序是无法改变的,详情请见小明补充的样例4
题目内容
某媒体处理服务负责接收来自多个媒体发送源的媒体包,并根据收到的媒体包进行媒体渲染处理。当前有这样一个需求:给定收到的媒体包序列号列表,计算发送该媒体包的最少发送源个数。
约束:
1.任意媒体包序列号seqs[i]满足:0≤seqs[i]≤65535
2.网络上没有重传媒体包,即:同一个发送源发送的媒体包序列号不会重复,且序列号每次加1(不考虑回绕问题,65535是发送源发送的最后一个媒体包序列号);如果收到的,媒体包序列号不满足该规则,说明这些媒体包必然来自于多个发送源。
3. 1≤seqs.length()≤105
输入描述
第一行:seqs列表长度n
第二行:seqs列表元素,元素之间通过空格隔开
输出描述
最少媒体包发送源个数
样例1
输入
11
1 2 3 4 5 6 7 8 9 10 10
输出
2
说明
媒体包发送源1:1 2 3 4 5 6 7 8 9 10
媒体包发送源2:10
媒体发送源个数为2,因此输出2
样例2
输入
5
65535 0 1 2 3
输出
2
说明
媒体包发送源1:65535
媒体包发送源2:0 1 2 3
媒体发送源个数为2,因此输出2
样例3
输入
18
1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10
输出
2
说明
媒体包发送源1:1 2 3 4 5 6 7 8 9 10
媒体包发送源2:2 3 4 5 6 7 8 9
媒体发送源个数为2,因此输出2
样例4
输入
8
2 2 2 2 1 1 1 1
输出
8