UVa11536 Smallest Sub-Array

本文介绍了解决UVa11536问题的优化枚举策略,通过选择最短长度子数组作为枚举对象,利用二分查找与滑动窗口法,实现O(NlogN)时间复杂度的高效算法。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

UVa11536 Smallest Sub-Array解题报告

题目链接
  又是一个优化枚举次数的题目,这次一定要记录下来,学会这种解题思路。

题目分析

  题目的意思是,需要在一个数组中,找到包含 [ 1 , K ] [1,K] [1,K]中所有整数的最短长度的子数组。
  当时我的思路是通过枚举起点,然后通过二分法来找到终点。然后超时了。。。。然后发现是我的枚举对象搞错了,以后解题要首先明确枚举对象是什么。
  根据网上的解题报告,可以选满足条件的最短长度作为枚举对象,然后二分查找,由于满足条件的长度 1 ≤ l e n ≤ N 1\leq len \leq N 1lenN ,每次枚举后通过滑动窗口来判断是否满足条件,时间复杂度为 O ( N ) O(N) O(N),所以总共的时间复杂度为 O ( N log ⁡ N ) O(N\log N) O(NlogN)

算法设计

  取枚举区间为左闭右开区间,初始L=1、R=N+1。如果当前枚举的值满足条件说明还可以继续往小了分,否则就应该往大了分。
  判断是否满足条件用滑动窗口法,假设当前枚举长度为 l e n len len,那么我们通过扫描一遍数组,每次只需要判断右边滑进来的元素、和左边滑出去的元素,这样就可以在 O ( N ) O(N) O(N)时间内得到结果。

完整代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 1000000 + 10000;
const int maxk = 1000+200;

int seq[maxn];

int cnt[maxk];
//前缀和


int N, M, K;

//尝试用长度为len的滑动窗口
bool judge(int len)
{
	//用来保存在滑动窗口中出现数字的总数
	int tot = 0;
	//cnt[i]记录出现数字i的次数
	memset(cnt, 0, sizeof(cnt));

	//滑动窗口
	for (int i = 1; i <= N; i++)
	{
		if (tot == K) return true;
		if (i == N + 1) return false;
		//滑动窗口新加入的元素
		if (seq[i] <= K&&cnt[seq[i]]++ == 0) tot++;
		//滑动窗口滑出去的元素
		if (i > len&&seq[i - len] <= K&&--cnt[seq[i - len]] == 0) tot--;
	}
	if (tot == K) return true;
	return false;
}


//二分枚举答案的长度
int solve()
{
	int L = 1;
	int R = N + 1;

	while (L < R)
	{
		int mid = (L + R) / 2;
		if (judge(mid)) R = mid;
		else L = mid + 1;
		//printf("mid = %d\n", mid);
	}
	if (L == N + 1) return -1;

	return L;
}

void init()
{
	

	seq[1] = 1;
	seq[2] = 2;
	seq[3] = 3;


	for (int i = 4; i <= N; i++)
	{
		seq[i] = (seq[i - 1] + seq[i - 2] + seq[i - 3]) % M + 1;
		
	}

}

int main()
{
	int T;
	int kcase = 1;
	freopen("C:\\Users\\lenovo\\Desktop\\test\\in.txt", "r", stdin);
	freopen("C:\\Users\\lenovo\\Desktop\\test\\out.txt", "w", stdout);
	scanf("%d", &T);

	while (T--)
	{
		scanf("%d%d%d", &N, &M, &K);
		//printf("N = %d M = %d K=%d\n", N, M, K);
		init();
		int min_len = solve();
		if (min_len > 0)
			printf("Case %d: %d\n", kcase++, min_len);
		else
			printf("Case %d: sequence nai\n", kcase++);
	}

	return 0;
}

以后记得选取正确的枚举对象~

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值