(Violence + Hash Table Optimization + Memoization Optimization) 2019 Blue Bridge Cup C/C++ Group A Zhenti 8 Modified Array

topic

Problem Description
  Given an array A = [A₁, A₂, · · · AN] of length N, there may be repeated integers in the array.
  Now Xiaoming wants to modify it into an array without repeating integers as follows. Xiao Ming will modify
  A₂, A₃, · · · , AN in turn.
  When modifying Ai, Xiaoming will check whether Ai has appeared in A₁ ∼ Ai−₁. If it has appeared, Xiaoming will add 1 to Ai; if the new Ai has appeared before, Xiaoming will continue to add 1 to Ai until Ai has not appeared in
  A₁ ∼ Ai−₁.
  When AN is also modified as above, obviously there are no repeated integers in the A array. Now given the initial A array, please calculate the final A array.
Input format
  The first line contains an integer N.
  The second line contains N integers A₁, A₂, · · · , AN .
Output format
  Output N integers, followed by the final A₁, A₂, · · · , AN.
Sample input
5
2 1 1 3 4
Sample output
2 1 3 4 5
Evaluation case size and convention
  For 80% of evaluation cases, 1 ≤ N ≤ 10000.
  For all evaluation cases, 1 ≤ N ≤ 100000, 1 ≤ Ai ≤ 1000000.

analyze

It's still the same, if you can't think of a good solution at first, consider the violent method first (it won't take long anyway), and use the current number and all the previous numbers to do an enumeration, and it will not end until the number does not change. Consider the time complexity, the outer loop traverses all numbers, O(n), and the inner loop considers the worst case, that is, the first n numbers happen to be consecutive from small to large, and then the current number just repeats the first number, We have to loop n times to solve this number. The total time complexity of all is about O(n^3). I have tried this complexity and only passed 40% of the evaluation data.

code section

Over 40% of the evaluation data

#include <bits/stdc++.h>
using namespace std; 

int main (void)
{
    
    
	int n;
	cin>>n;
	vector<int> arr(n);
	for(int i=0;i<n;i++)
		cin>>arr[i];
	for(int i=1;i<n;i++)	//nmax:1e5
	{
    
    
		bool flag;
		do{
    
    
			flag=false;
			for(int j=i-1;j>=0;--j)		
			{
    
    
				if(arr[i]==arr[j])
				{
    
    
					flag=true;
					arr[i]++;
				}
			}
		}while(flag);
	}
	for(int i=0;i<n;i++)
		cout<<arr[i]<<" ";
		
	return 0;
}

If you can't think of an optimization method, the price/performance ratio of violence is also very high.

1. The first optimization (hash table)

At this time, let's consider the situation of repetition. We have to loop every time to find the repeated number in front of it. At this time, we can think of using the hash table to directly find out whether there is repeated data in front of it. If there is, we will always add one to find it. If there is no position, note that this is to find repetitions in all the numbers in front of it, so you have to enter a number to judge one, otherwise it is equivalent to looking for repeated numbers in all the numbers. Although the final result is the same, the number of the order is different

#include <bits/stdc++.h>
using namespace std;

int hash_map[2000000];		//哈希表 

int main (void)
{
    
    
	int n;
	cin>>n;
	
	memset(hash_map,0,sizeof(hash_map));
	for(int i=0;i<n;i++)
	{
    
    
		int x;
		cin>>x;
		while(hash_map[x])
			x++;
		hash_map[x]=1;
		cout<<x<<" "; 
	}
	
	return 0;
}

Consider the time complexity in this case, the outer loop is O(n), the worst case for the inner loop is probably all numbers are the same, which is about O(n), and the total time complexity is about O( n^2), given that 80% of the data range of n in the question is 1e4, this 80% should be stable, and 100% of the data is 1e5, then our worst running times is probably 1e10. It is about to time out.
I submitted it. The evaluation data of the Blue Bridge Cup did not disappoint me. One of the 10 data did not exceed the time out.

2. Second optimization (memoization)

If I want to continue to optimize, I want to save the repeated steps (pruning). Later, I thought of for example, if there are many identical repeated elements in the array, for example, 1, 1, 1, 1, 1, 1, then we will each Each time, we need to calculate the position where it should be placed from 1 one by one, then we can open an array and store the position where the previous 1 was placed. When the next 1 appears, we will directly place it in the next position of the previous one. You can start the comparison at the same place, which has the effect of memorization. Using space to exchange time, the time complexity has not changed, but the worst case is optimized.

#include <bits/stdc++.h>
using namespace std;

int hash_map[2000000];		//哈希表
int cache [2000000];	//(剪枝)储存最近的一次相同数字存储的位置 

int main (void)
{
    
    
	int n;
	cin>>n;
	
	memset(hash_map,0,sizeof(hash_map));
	memset(cache,-1,sizeof(cache));
	for(int i=0;i<n;i++)
	{
    
    
		int x;
		cin>>x;
		int tmp=x; 
		while(hash_map[x])
		{
    
    
			if(cache[x]!=-1)
				x=cache[x]+1;
			else
				x++;
		}
		hash_map[x]=1;
		cache[tmp]=x;
		cout<<x<<" "; 
	}
	
	return 0;
}

875ms have passed the use case just now, I glanced at the use case n is 1e5, the maximum value, I haven't thought of the optimization method for the time being

Summarize

Like this algorithm problem, the violent method is very good, but in general, it cannot pass all the evaluation data. Estimate the time complexity. If it is not enough, you want to repeat the situation to optimize. If you estimate the time complexity after optimization It will be a lot worse, so we need to replace it with another method as a whole.

Guess you like

Origin blog.csdn.net/weixin_46035615/article/details/123985364