FOJ Problem 2216 The Longest Straight

Problem Description
ZB is playing a card game where the goal is to make straights. Each card in the deck has a number between 1 and M(including 1 and M). A straight is a sequence of cards with consecutive values. Values do not wrap around, so 1 does not come after M. In addition to regular cards, the deck also contains jokers. Each joker can be used as any valid number (between 1 and M, including 1 and M).

You will be given N integers card[1] .. card[n] referring to the cards in your hand. Jokers are represented by zeros, and other cards are represented by their values. ZB wants to know the number of cards in the longest straight that can be formed using one or more cards from his hand.

Input
The first line contains an integer T, meaning the number of the cases.

For each test case:

The first line there are two integers N and M in the first line (1 <= N, M <= 100000), and the second line contains N integers card[i] (0 <= card[i] <= M).

Output
For each test case, output a single integer in a line -- the longest straight ZB can get.

Sample Input
2
7 11
0 6 5 3 0 10 11
8 1000
100 100 100 101 100 99 97 103 Sample Output
5

3

 

题意:先说一下大概的题意,就是说给你一些牌,牌上有对应的数值,另外有一些joker,这些可以替换成任意数值的牌,让你用手上的牌(包括joker)构造出一段最长的连续序列

解题思路:以下代码的数据存储的的方法是用0, 1表示的,a[k]==1表示有k这张牌,反之则没有。首先一种最笨而一定能求出来的方法就是枚举起点i跟终点j,然后统计i到j之间的空缺数,若空缺数小于等于joker数量,记录i~j的区间长度,判断能否对Max经行更新。然后看一下m的数量达到100000,用这种方法枚举起点跟终点的复杂度为O(m*m),加上计算空缺数,总的时间复杂度高达O(m*m*m),超时是毫无疑问的。但并不代表这种方法不可行。首先先从统计i~j区间的空缺数上优化,在这边构造一个s数组,s[k]表示1~k的空缺数,那么i到j的空缺数直接就是s[j]-s[i-1],s的构造需要O(m), 构造完s后求空缺的时间复杂度为O(1),所以总的时间复杂度降为O(m*m), 但是对已m=100000还是超时的,所以看看时间复杂度能不能降到O(m)。现在回到i, j上面来,明白一点初始时j=i+Max, 然后在移动i, j的过程中有一点很关键就是当对i经行i++时,对应j一定要增大,即对应要有j++, 因为这样才能保证i~j的区间长度不小于Max,因为题目最终是要求i~j的最大区间长度,你再去计算比Max小的区间长度是没有必要的。另外j到达m时就要终止了, 应为joker只能变为1~m之间的数,这时候你考虑大于m的数是没有意义的。在这个过程中区间右端点是不断向右移动的,时间复杂度最终降为O(m), 所以可以通过了。

 

AC代码:

 

 

# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
int s[100010], a[100010];
int main(){
	int t, n, i, j, k, m, cnt, temp, Max;
	scanf("%d", &t);
	for(i=1; i<=t; i++){
		scanf("%d%d", &n, &m);
		cnt=0;
		memset(a, 0, sizeof(a));
		for(j=1; j<=n; j++){
			scanf("%d", &temp);
			if(temp==0){
				cnt++;
			}
			else{
				a[temp]=1;
			}
		}
		s[0]=0;
		for(j=1; j<=m; j++){
			if(a[j]==0){
				s[j]=s[j-1]+1;
			}
			else{
				s[j]=s[j-1];
			}
		}
		Max=max(1, cnt);
		if(Max>m){
			Max=m;
		}
		j=1;k=j+Max;
		while(k<=m){
			if(s[k]-s[j-1]<=cnt){
				Max=max(Max, k-j+1);
				k++;
			}
			else{
				j++;k++;
			}
		}
		printf("%d\n",  Max);
	}
	return 0;
}

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值