倪文迪陪你学蓝桥杯2021寒假每日一题:1.30日(2019省赛A组第8题)

2021年寒假每日一题,2017~2019年的省赛真题。本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。每日一题,关注蓝桥杯专栏: https://blog.csdn.net/weixin_43914593/category_10721247.html

每题提供C++、Java、Python三种语言的代码。

文章目录

  • 1、题目描述
  • 2、题解
    • 2.1 暴力
    • 2.2 hash
    • 2.3 并查集
      • 2.3.1 Python代码
      • 2.3.2 C++代码
      • 2.3.3 Java代码

2019省赛A组第8题“修改数组” ,题目链接:
http://oj.ecustacm.cn/problem.php?id=1459
https://www.dotcpp.com/oj/problem2301.html

1、题目描述


给定一个长度为N 的数组A = [A1, A2,…,AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改A2, A3, …, AN。
当修改Ai 时,小明会检查Ai 是否在A1~ Ai-1 中出现过。
如果出现过,则小明会给Ai 加上1 ;
如果新的Ai 仍在之前出现过,小明会持续给Ai 加1 ,直到Ai 没有在A1~Ai-1中出现过。
当AN 也经过上述修改之后,显然A数组中就没有重复的整数了。
现在给定初始的A 数组,请你计算出最终的A 数组。
输入:第一行包含一个整数N(1<=N<=100000)
第二行包含N个整数A1,A2,…,AN(1<=Ai<=1000000)
输出:输出N个整数,依次是最终的A1,A2,…,AN


2、题解

2.1 暴力

   先尝试暴力的方法:每读入一个新的数,就检查前面是否出现过,每一次需要检查前面所有的数。共有 n n n个数,每个数检查 O ( n ) O(n) O(n)次,所以总复杂度是 O ( n 2 ) O(n^2) O(n2)的,超时。

2.2 hash

   容易想到一个改进的方法:用hash。定义 v i s [ ] vis[] vis[]数组, v i s [ i ] vis[i] vis[i]表示数字 i i i是否已经出现过。这样就不用检查前面所有的数了,基本上可以在 O ( 1 ) O(1) O(1)的时间内定位到。
  然而,本题有个特殊的要求:“如果新的 A i A_i Ai仍在之前出现过,小明会持续给 A i A_i Ai加1 ,直到 A i A_i Ai没有在 A 1 A_1 A1 ~ A i − 1 A_{i-1} Ai1中出现过。”这导致在某些情况下,仍然需要大量的检查。以5个9为例:A[]={9, 9, 9, 9, 9}。
   第一次检查A[1]=9,设置vis[9]=1;
   第二次检查A[2]=9,先查到vis[9]=1,则把A[2]加1,变为a[2]=10,设置vis[10]=1;
   第三次检查A[3]=9,先查到vis[9]=1,则把A[3]加1得A[3]=10;再查到vis[10]=1,再把A[3]加1得A[3]=11,设置vis[11]=1;
   …
   复杂度仍然是 O ( n 2 ) O(n^2) O(n2)的。
   下面给出hash代码。

#include
using namespace std;
#define N 1000002
int vis[N]={
     0};   //hash:  vis[i]=1表示数字i已经存在
int main(){
     
	int n;	scanf("%d",&n);
	for(int i=0;i<n;i++){
     
		int a;
		scanf("%d",&a);  //读一个数字
		while(vis[a]==1) //若a已经出现过,则该数加1。若加1后再出现,则继续加
			a++;
		vis[a]=1;        //标记该数字
		printf("%d ",a); //打印
	}
}

2.3 并查集

   上文提到,本题用Hash方法,在特殊情况下仍然需要大量的检查。问题出在“持续给 A i A_i Ai 加1 ,直到 A i A_i Ai没有在 A 1 A_1 A1 ~ A i − 1 A_{i-1} Ai1中出现过”。也就是说,问题出在那些相同的数字上。当处理一个新的 A [ i ] A[i] A[i]时,需要检查所有与它相同的数字。
   如果把这些相同的数字看成一个集合,就能用并查集处理。
   这种并查集,必须是用“路径压缩”优化的,才能加快检查速度。没有路径压缩的并查集,仍然超时。
   学习并查集和“路径压缩”,参考博文:https://blog.csdn.net/weixin_43914593/article/details/104108049

2.3.1 Python代码

N=1000002
s=[]   #并查集
def find_set(x):    #有路径压缩优化的查询
    if(x != s[x]):
       s[x] = find_set(s[x])     #集的根是最新的一个数
    return s[x]
 
n=int(input())
A=str(input()).split(' ')
for i in range(N):  #并查集初始化
    s.append(i)
for i in range(n):
    A[i]=int(A[i])
    root=find_set(A[i])
    A[i]=root
    s[root]=find_set(root+1)  #加1
for i in A:
    print(i,end=' ')

2.3.2 C++代码

#include
using namespace std;

const int N=1000002;
int A[N];
int s[N];  //并查集

int find_set(int x){
                 //用“路径压缩”优化的查询
    if(x != s[x])
       s[x] = find_set(s[x]);   //路径压缩
    return s[x];
}

int main(){
     
    for(int i=1;i<N;i++)        //并查集初始化
        s[i]=i;
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
     
        scanf("%d",&A[i]);
        int root = find_set(A[i]);    //查询到并查集的根
        A[i] = root;
        s[root] = find_set(root+1);   //加1
    }

    for(int i=1;i<=n;i++)
        printf("%d ",A[i]);
    return 0;
}

2.3.3 Java代码

//oj.ecustacm.c User: 20180861115
import java.io.BufferedReader;
import java.io.InputStreamReader;
 
public class Main {
     
    static int max = 1000005;
    static int num[] = new int[max];
    public static int find(int x) {
     
        if(x == num[x]) {
     
            return x;
        }
        return num[x] = find(num[x]);
    }
    public static void main(String[] args)  {
     
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try {
     
            int n = Integer.parseInt(in.readLine());
            int k = 0;
            String[] s = in.readLine().split(" ");
            for(int i=1;i<=1000000;i++) {
     
                num[i] = i;
            }
            k = Integer.parseInt(s[0]);
            k = find(k);
            num[k] = find(k+1);
            System.out.print(k);
            for(int i=1;i<n;i++) {
     
                k = Integer.parseInt(s[i]);
                k = find(k);
                System.out.print(" "+k);
                num[k] = find(k+1);
            }
        }catch (Exception e) {
     
            e.printStackTrace();
        }
    }       
}

你可能感兴趣的:(蓝桥杯每日一题)