原题目链接:http://www.51weixue.com/thread-678-1-1.html
题目:
给定一个长度为n的非负整数序列n<=100000,只能进行一种操作:选择一个数(不是最后一个)把ai和a(i+1)同时减少1。 问能否经过有限次这样的操作把这个数列变为全0的数列。 输入:长度为n的数组 每个数是正整数 输出:yes 或 no |
原来我的解法是错的。。。2 3 3 2,这个序列是可以消去的。。
我的解法,大概就是类似于分治法的思想,将一个数列拆分一个一个小数列,再分别求解:
1、先从简单的几个测试示例找规律,将问题简单化。
当n = 2时,只有两个元素大小相同时,数列才可以变为全0的数列。
当n = 3时,只有当中间元素的大小等于首尾元素大小之和时,数列才可以变为全为的数列。
当n = 4时,可以将这4个元素拆分为两组,也就变成了两个n = 2的情况。所以,可以根据n = 2。
同理,当n = 5时,可以将这5个元素拆分为一个由3个元素组成的小数列和一个由2个元素组成的小数列,再分别求解。
所以当n > 3时,可以将n为解成多个由2个元素和3个元素组成的小数列,然后分别求解,最后合并结果。
一开始是先想到递归解法的,不过既然知道可以拆成由2个元素或者3个元素小数列,那么就可以直接写出非递归解法。
#include<iostream>
#include<cstdio>
const int N = 100000 ;
bool AllToZero(int *A,int n) ; //递归解法
bool All2Zero(int *A ,int n) ; //非递归解法
int main(void)
{
int i,n ;
int A[N] ;
freopen("in.txt","r",stdin) ;
while(scanf("%d",&n) != EOF)
{
for(i = 0 ; i < n ; ++i)
{
scanf("%d",&A[i]) ;
}
if(true == AllToZero(A,n))
{
printf("Yes\n") ;
}
else
{
printf("No\n") ;
}
}
return 0 ;
}
/*递归版本*/
bool AllToZero(int *A,int n)
{
if(n > 1)
{
return (A[n-1] == A[n-2] && AllToZero(A,n-2)) || (n-3 >= 0 && A[n-2] == A[n-1] + A[n-3] && AllToZero(A,n-3));
}
else if(0 == n)
{
return true ;
}
return false ;
}
/*非递版本*/
bool All2Zero(int *A,int n)
{
int i = 0 ;
while(i < n)
{
if(i+1 < n && A[i] == A[i+1])
{
i += 2 ;
}
else if(i+1 < n && i+2 < n && A[i+1] == A[i] + A[i+2])
{
i += 3 ;
}
else //一旦发现不能变成0的小数列,立马返回
{
return false ;
}
}
return true ;
}
测试数据:
9
1 2 1 4 5 1 2 3 1
5
2 2 3 2 2
8
1 2 1 4 5 1 1 1
6
1 2 1 4 5 1
9
1 2 1 1 1 1 1 1 1
2
2 2
3
4 1 6
4
2 6 4 4
1
4
5
1 1 1 1 1
附上别人的一个更好的思路:
最后一个数只能和倒数第二个数同时消去,倒数第二个和最后一个消去之后,只能和倒数第三个同时消去。
按照这个思路,从最后一个数开始,a[i-1] -= a[i],当i-1!=1且a[i-1] < 0时,无解。最后看a[0]是不是为0,如果为0,则yes,否则为no。