蓝桥杯 — — 混乱的数组

混乱的数组

题目:

思路:

题目的基本意思是:给定一个数字x,找出一个数组,要求数组中有x对下标小的数其值更大的数对。我们可以利用逆序对来进行求解,如:3, 2, 1其逆序对分别为:2, 1, 0因此数组3, 2, 1的逆序对之和为3。

我们知道,逆序排序的序列的逆序对最大,并且逆序排列的序列求的逆序对的公式为: n × ( n − 1 ) 2 \frac{n\times(n - 1)}{2} 2n×(n1),其实这也是一个组合问题:如3, 2, 1其逆序可以这样思考:从这个序列中任意选择两个数,并且要求不重复,有:32,31,21这三个,可能会有人有这样的疑惑,如果是23,13,12是不是就不满足题目要求了?要知道,我们求的只是其组合个数,不用管其排列顺序,可以设定默认是按照从大到小的进行排列的。

求最大逆序对的程序

方式一:

int C(int n){
	return (n * (n - 1)) / 2;
}

方式二:

int C(int n){
	int ans = 0;
	for(int i = 1;i < n;i++){
		ans += i;
	}
	return ans;
} 

利用求最大逆序对,我们可以求出数组中有多少个数,即确定了结果中数字的个数。

如:7个数的最大逆序为:21,8个数的最大逆序为28

如果我们要求一个数的逆序为25,那么便确定了结果一共有8个数字,如:[8, 7, 6, 5, 1, 3, 2 ,1](题目要求只包含正整数)

显然这个结果满足逆序为25,但是不满足题目要求:最小字典序,我们可以继续进行优化,最终得到:[5, 4, 3, 3, 2, 2, 1, 1],满足最小字典序:

逆序对满足逆序对的最小字典序列
22265432 11
233543 2211
2444332211
2554 332211
266543 2211
27765432 11
2887654321

通过观察以上的表格,我们可以观察出以下规律:

  1. 如果给出的逆序对大于等于 ( C n 2 + C n − 1 2 ) 2 \frac{(C_n^2 + C_{n - 1}^2)}{2} 2(Cn2+Cn12),那么满足以下规律,斜体部分都是严格递减的,我们可以从后向前进行填充,先填充成对出现的(填充的个数为 C n 2 − x C_n^2 - x Cn2x个,x为需要求的逆序对的个数,即题目给出的x),并且使用一个变量用于计数,如果没有填充完毕,就进行严格的单调递增的进行填充,如果填充的长度满足求出的长度就结束填充,最后逆向输出即可。(如:逆序对为25,那么需要填充$C_8^2 - 25 = 28 - 25 = 3 $个成对出现的数字,然后再进行单调递增的填充4,5,6最后逆向输出即可)

  2. 如果给出的逆序对小于 ( C n 2 + C n − 1 2 ) 2 \frac{(C_n^2 + C_{n - 1}^2)}{2} 2(Cn2+Cn12),满足以下规律:依然是从后向前进行填充,其中重复数字部分填充 x − C n − 1 2 x - C_{n - 1}^2 xCn12次,填充完毕之后进行严格单调的填充,不填充最后一个数字,最后一个数字与 ( x − C n − 1 2 ) × 2 (x-C_{n - 1}^2) \times 2 (xCn12)×2个位置的值相等,即表格中加粗的部分的数字。(如:逆序对为23,那么需要填充 23 − C 7 2 = 2 23 - C_7^2 = 2 23C72=2次重复的数字,然后单调递增的进行填充到总长度-1个位置,最后一个位置的值等于 ( 24 − C 7 2 ) × 2 = ( 24 − 21 ) × 2 = 2 × 2 = 4 (24-C_7^2) \times 2 = (24 - 21) \times 2 = 2 \times 2 = 4 (24C72)×2=(2421)×2=2×2=4位置的值,实际上是第5个位置,因为数组的下标是从0开始的)

部分代码实现
		// i表示已经找到的数组的长度
		int oi = C(i - 1);
		int cut = (C(i) + oi) / 2;  // 中间的情况
		if(l < cut){
			// 短填充
			int tempc = l - C(i - 1);
			int idx = tempc * 2;
			int num = 1;
			int numlen = 0;
			while(numlen < i - 1){
				if(tempc > 0){
					ans.push_back(num);
					ans.push_back(num);
					tempc--;
					num ++;
					numlen += 2;
				}else{
					ans.push_back(num);
					num++;
					numlen += 1;
				}
				
			}
			ans.push_back(ans[idx]);   // 最后一位数字 
			 
		}else{
			// 长填充
			int tempc = C(i) - l ;
			int num = 1;
			int numlen = 0;
			while(numlen < i){
				if(tempc > 0){
					ans.push_back(num);
					ans.push_back(num);
					tempc--;
					numlen += 2;
				}else{
					ans.push_back(num);
					numlen += 1;
				}
				num++;
			}
		}

代码:

// 混乱的数组
#include<iostream>
using namespace std;
#include<vector>

// 求C_n^2  如果是选择2个数进行组合,数学公式(n * (n - 1)) / 2
int C(int n){
	return (n * (n - 1)) / 2;
}

//int C(int n){
//	int ans = 0;
//	for(int i = 1;i < n;i++){
//		ans += i;
//	}
//	return ans;
//} 

void solve(){
	int l = 0;
	cin>>l;
//	cout<<C(5)<<endl; 
	int i = 2;
	bool flag = false;
	while(true){
		if(C(i) == l){
			flag = true;
			break;
		}else if(C(i) > l){
			break; 
		}else{
			i++;
		}
	}
	
	cout<<i<<endl;
	if(flag){   // 表示找到了数组
		for(int k = i;k > 0;k --) cout<<k<<' '; 
	}else{
		vector<int> ans;
		int oi = C(i - 1);
		int cut = (C(i) + oi) / 2;  // 中间的情况
		if(l < cut){
			// 短填充
			int tempc = l - C(i - 1);
			int idx = tempc * 2;
			int num = 1;
			int numlen = 0;
			while(numlen < i - 1){
				if(tempc > 0){
					ans.push_back(num);
					ans.push_back(num);
					tempc--;
					num ++;
					numlen += 2;
				}else{
					ans.push_back(num);
					num++;
					numlen += 1;
				}
				
			}
			ans.push_back(ans[idx]);   // 最后一位数字 
			 
		}else{
			// 长填充
			int tempc = C(i) - l ;
			int num = 1;
			int numlen = 0;
			while(numlen < i){
				if(tempc > 0){
					ans.push_back(num);
					ans.push_back(num);
					tempc--;
					numlen += 2;
				}else{
					ans.push_back(num);
					numlen += 1;
				}
				num++;
			}
		}
		//进行逆序输出结果数组
		for(int i = ans.size() - 1;i >= 0;i --){
			cout<<ans[i]<<' '; 
		}
	}
	
	return ;
}


int main(){
	int t = 1;
	while(t--){
		solve();
	} 
	
	return 0;
}

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值