一道很有趣的题,有很多有趣的想法,也是想了很多东西,各种优化之后终于把这题解决了,还是有些东西可写的。
说说思路,可以发现如果前面两个数字确定的话,那么这个数列就已经确定了,那么寻找就是一个o(n)的复杂度,因为前两个数字有n方种可能性,所以复杂度在n的三次方,这样会发现直接写是不行的,那么就需要若干的优化,才能解决这个问题。先考虑这个数列到底多大,会发现其实并不会太大,因为斐波那契数列是指数级增长的(除了都是0的情况),另外如果后面已经没有更大的了也可以直接结束,不过这些都是小的,基本没什么用,核心的优化在于数字的重复性,如果出现了相同的两个数字,那么肯定是后面的比前面的小,所以不需要继续算了,另外同一个斐波那契数列不需要算很多遍,所以如果一个情况是之前情况的子列也不需要计算了。反正经过了这些优化之后,总算是解决了这个问题。
至于怎样把重复的情况快速确认,之前用的是set储存,复杂度logn结果炸了,之后忽然发现其实并不需要set,因为只有1000个数字所以情况不多于1000000种,直接用一个坐标离散化即可。
贴代码
# include <stdio.h>
# include <string.h>
# include <algorithm>
using namespace std;
typedef unsigned long long ll;
typedef pair<ll , ll> P;
const int MAX_N = 1000;
const ll INF = (ll)1 << 63;
bool used[MAX_N][MAX_N];
ll A[MAX_N];
int N;
ll max(ll a , ll b)
{
return (a > b) ? a : b;
}
int main()
{
while(~scanf("%d", &N))
{
memset(used , 0 , sizeof(used));
P B[MAX_N];
int C[MAX_N];
ll zd = 0;
int i, j, k;
int n0 = 0;
for(i = 0 ; i < N ; i++)
{
scanf("%llu", &A[i]);
zd = max(A[i] , zd);
if(!A[i])
n0++;
B[i].first = A[i];
B[i].second = i;
}
sort(B , B + N);
int num = 0;
for(i = 0 ; i < N ; i++)
{
if(i > 0 && B[i].first > B[i - 1].first)
num++;
C[B[i].second] = num;
}
int ma = 0, l, r;
for(i = 1 ; i < N ; i++)
{
if(N - i + 1 < ma)
break;
for(j = 0 ; j < i ; j++)
{
if(used[C[j]][C[i]] || (!A[j] && !A[i]))
continue;
used[C[j]][C[i]] = 1;
int sum = 2, q2 = j, q1 = i;
for(k = i + 1 ; k < N ; k++)
{
if(A[q2] + A[q1] > zd)
break;
if(A[k] == A[q2] + A[q1])
{
q2 = q1;
q1 = k;
sum++;
used[C[q2]][C[q1]] = 1;
}
}
if(sum > ma || (sum == ma && P(A[l] , A[r]) > P(A[j] , A[i])))
{
ma = sum;
l = j;
r = i;
}
}
}
if(n0 > ma)
{
printf("%d\n", n0);
for(i = 0 ; i < n0 ; i++)
printf("0%s", (i == n0 - 1) ? "\n" : " ");
continue;
}
printf("%d\n", ma);
printf("%llu %llu%s", A[l], A[r], (ma == 2) ? "\n" : " ");
int sum = 2, q2 = l, q1 = r;
for(k = r + 1 ; k < N ; k++)
{
if(A[k] == A[q2] + A[q1])
{
q2 = q1;
q1 = k;
sum++;
printf("%llu%s", A[k], (sum == ma) ? "\n" : " ");
}
if(sum == ma)
break;
}
}
return 0;
}