连号区间数(2013年第四届c/c++ b组第10题)

题目描述

标题:连号区间数

小明这些天一直在思考这样一个奇怪而有趣的问题:

在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:

如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。

当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式:
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。

输出格式:
输出一个整数,表示不同连号区间的数目。

示例:
用户输入:
4
3 2 4 1

程序应输出:
7

用户输入:
5
3 4 2 5 1

程序应输出:
9

解释:
第一个用例中,有7个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [2,2], [3,3], [4,4]
第二个用例中,有9个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [1,5], [2,2], [3,3], [4,4], [5,5]

 

解题过程(其实这道题很简单,请没思路的朋友耐心

看下去,也可以直接先看最后面的代码,很短,看完

代码或许你就懂了)

 

首先考虑时间复杂度:数据规模N<=50000,而我们要解这道题必定要遍

历所有的子数组,所以时间复杂度最大为N(N-1)/2,大约是12.5亿,也

就是N最大时程序要运行12秒(这还是保守估计),因为计算机每秒我

们都认为计算一亿次左右,而题目要求要在5秒之内完成,这几乎不可能

完成的,所以我们猜测实际出题者实际给出的样例并不太接近50000。

 

 

走不通的动态规划思路:

一开始解决这道题的思路是“动态规划”,所以一直在思考较小的区间与包含这个区间的较大区间之间的联系:

考虑以下样例:

[3, 4, 2, 5, 1]

显然对于包含单个元素的子数组来说肯定符合连号区间的定义,即[3],[4],[2],[5],[1]都是连号区间。

那我们来考虑[3,4]是不是连号区间是否与[3],[4]是连号区间有关,这里[3,4]、[3]、[4]恰好都是

连号区间;

继续考虑[4,2]是不是连号区间是否与[4]、[2]是连号区间有关,显然虽然[4]和[2]都是连号区间,

[4,2]并不是连号区间,经过仔细考虑,我找不到任何可以用动态规划的痕迹,所以转换了思路。

 

转换思路,考虑暴力破解?用哪种方法判断一个子数组是否符合

连号区间开销最小?

还是考虑[3, 4, 2, 5, 1]这个样例,我们先把它排个序,这样看起来直观些:

[1, 2, 3, 4, 5]

由于数据元素是有序且唯一的(不存在一个数组里面有两个元素相等),所以我们会惊喜地发现

当一个数组是连号区间时,有:

数组最大值 - 数组最小值 + 1 = 数组元素个数

我们记为: max - min + 1 = len;

证明max - min + 1 = len

假设一个长度为len的数组为A = {a1, a2, a3, ..., an};

我们干脆就假设数组最小元素min = a1,假设

数组最大元素max = an;

那怎样使得A数组满足连号区间呢,根据

连号区间的定义,该数组必须要有min + 1, min + 2,...,

max - 1这些数组元素(一共max - min - 1 个数),

这样就要求数组元素必须有(max - min + 1)

个数(包括最大元素和最小元素)才能满足连号区间。

而实际上A数组不一定是有max - min + 1个数组元素的,

这种情况下A数组就不满足连号区间。

举个栗子

如果你还不明白我在说什么,那应该是我表达能力太差了,

这样吧,我们举个栗子:

考虑以下数据

[3, 4, 2, 7, 1]

这个数组最大值为7,最小值为1,那你想一想,如果要让该数组满足

连号区间的定义,必然还要有2,  3,  4, 5, 6这几个数组元素才满足对吧,

即数组元素个数必定是7个元素(包括最大值和最小值)才满足连号

区间,而[3, 4, 2, 7, 1]实际才有5个元素,这样明显[3, 4, 2, 7, 1]就不符合

连号区间的定义。

 

accept代码

 

#include <cstdio>
#include <iostream>
using namespace std;
int num[50000 + 10];


int main()
{
	int count = 0;
	int N;
	cin >> N;
	count += N;  //只有一个元素的子数组明显符合条件 ,一共有N个只有一个元素的子数组 
	for(int i = 0; i < N; i++)
	{
		cin >> num[i];
	}

	//遍历所有子数组,如果子数组满足“最大元素 - 最小元素 + 1 == 数组长度”,则说明满足连号区间条件,count++。 
	for(int i = 0; i < N; i++)
	{
		int min = num[i];
		int max = num[i];
		for(int j = i + 1; j < N; j++)
		{
			if(min > num[j])
			{
				min = num[j];
			}
			if(max < num[j])
			{
				max = num[j];
			}
			if(max - min == j - i)
			{
				count++;
			}
		}
	}
	cout << count << endl;
	
	return 0;
}

 

  

 如果哪里总结错了,希望各位朋友不吝赐教!!!

 

转载于:https://www.cnblogs.com/linguosen/p/10534627.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值