【xdoj难题集】1131 A Fib Sequences

题目链接

一道很有趣的题,有很多有趣的想法,也是想了很多东西,各种优化之后终于把这题解决了,还是有些东西可写的。

说说思路,可以发现如果前面两个数字确定的话,那么这个数列就已经确定了,那么寻找就是一个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;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值