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; }