题目大意是给定一串0,1数列,允许删除一个0,求数列的1的最长子序列长度。
心路历程:
乍一看感觉这题目不难,毕竟也曾经做过几道子序列问题。但写起代码来却是这里一个坑,那里一个坑。
-
一开始的想法是尝试把删除每个0的情况考虑一遍,计算对应的子序列长度就可以得出答案。这种方法当然不是不可以,实现起来其实。。也有点困难,因为数列没有删除功能(我不确定c++的模版有没有哇,默认没有),所以每次删除都是一个大工程,就算实现了时间复杂度也是O(n^2),所以直接排除。既然看0不行,
-
那看1总可以吧。从头遍历数组,对遇到的第一个1子序列进行计数,到达末端后尝试删除0并接着往后数1。这个办法有几个难点:
- 如果单纯的用遍历来实现的话,第一次删除0并接着往后计数的时候需要知道删除的机会已经被用掉
- 删除完0接着往后走,虽然得到了当前最大子序列,但却有可能丢失后面的1与更后面的子序列组合而成的更长的子序列。
解决办法其实也不难,遍历数组中,在一串1后遇到0,且判断0的后面紧接着1,就嵌套一个循环,循环从该1子序列后的1开始接着数1,直到下一个0,出循环,获得当前最长序列长度。也算是变相的尝试所有组合了。下面是代码:
int maxOnes(int* a, int size)
{
int MaxNums = 0;
int tmpMaxNums = 0;
int i = 0;
while(a[i]==0) i++;
for(; i < size;)
{
if(a[i] == 1)
tmpMaxNums++;
else if(a[i] == 0 && i+1 < size && a[i+1] == 1)
{
for (int j = i+1; a[j] != 0 && j < size; j++) {
tmpMaxNums++;
}
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
tmpMaxNums = 0;
}
else {
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
tmpMaxNums = 0;
while(a[i]==0) i++;
continue;
}
++i;
}
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
return MaxNums;
}
我也不知道为什么一开始的我一直执着于用递归实现,导致代码的bug很多,真的服了自己,下面是递归的代码,又丑又长:
int maxOnes(int* a, int index, int size, bool chance)
{
int MaxNums = 0;
int tmpMaxNums = 0;
int i = index;
while(a[i]==0) i++;
for(; i < size;)
{
if(a[i] == 1)
tmpMaxNums++;
else if(a[i] == 0 && i+1 < size && a[i+1] == 1 && chance)
{
tmpMaxNums += maxOnes(a, i+1, size, false);
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
tmpMaxNums = 0;
}
else if(a[i] == 0 && !chance)
{
break;
}
else {
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
tmpMaxNums = 0;
while(a[i]==0) i++;
continue;
}
++i;
}
if(tmpMaxNums > MaxNums)
{
MaxNums = tmpMaxNums;
}
return MaxNums;
}
就是因为一开始一直想写成递归才害我这题没写出来的。。气死了
之前没怎么刷过算法题,但个人总感觉这题可以用动态规划解决,毕竟我这个方案复杂度其实也近O(n^2)了(毕竟暴力解法),但实在没想到什么办法可以优化,想用空间换时间,却并想不出什么好办法。。有懂的小伙伴一起探讨呀,不得不说做题还是蛮快乐的。