题意:在给定的数组里,寻找一个最长的序列,满足ai-2+ai-1=ai。并输出这个序列。

很容易想到一个DP方程

dp[i][j]=max(dp[k][i])+1. (a[k]+a[i]==a[j],1<=k&&k<i)

dp[i][j]表示序列最后两位是a[i],a[j]时的最长长度。

这个方程状态是O(n^2),转移是O(n),总复杂度是O(n^3)会超时。

进一步思考会发现转移这里是可以优化的。实际上我们只需要知道离i最近的那个满足a[k]+a[i]==a[j]的k就行,即最大k。

举个例子

2 3 -1 2 1 3

当i=5,j=6,即ai=1,aj=3时,这时需要找一个ak=2的,显然a1和a4满足,我们会选择a4而不是a1。因为可以转移到a1的状态一定都可以转移到a1,但能转移到a4的状态却不一定都能转移到a1,因此dp[4][5]>=dp[1][5]。这样我们只需要在遍历数组的时候维护数组每个数的最大的下标即可。这里使用hash来做。

我用了stl的hash_map。最后需要逆序输出结果。另外注意卡内存,要用short。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<hash_map>
#include<ext/hash_map>
using namespace std;
using __gnu_cxx::hash_map;
hash_map <int,short> has;
];
][];
void output(int n,int x,int y)
{
    if(!n) return ;
    output(n-,y-x,x);
    ) printf("%d",y-x);
    else printf(" %d",y-x);
}
int main()
{
    int n;
    bool blank=false;
    while(scanf("%d",&n)!=EOF)
    {
        if(blank) printf("\n");
        else  blank=true;
        memset(dp,,sizeof(dp));
        ; i<=n; ++i)
        {
            scanf("%d",&a[i]);
            ; j<i; ++j)
                dp[j][i]=;
        }
        )
        {
            printf(]);
            continue;
        }
        has.clear();
        ,x=a[],y=a[];
        ; i<=n; ++i)
        {
            ; j<=n; ++j)
            {
                hash_map<int,short>::iterator it=has.find(a[j]-a[i]);
                if(it!=has.end())
                    dp[i][j]=dp[it->second][i]+;
                if(dp[i][j]>ans)
                {
                    ans=dp[i][j];
                    x=a[i];
                    y=a[j];
                }
            }
            has[a[i]]=i;
        }
        printf("%d\n",ans);
        ans-=;
        output(ans,x,y);
        if(ans) printf(" ");
        printf("%d %d\n",x,y);
    }
    ;
}

当然也可以定义状态 dp[i][j]为序列开头两个数字是ai,aj的最长长度。这样就可以正序输出了。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<hash_map>
#include<ext/hash_map>
using namespace std;
using __gnu_cxx::hash_map;
hash_map <int,short> has;
];
][];
int main()
{
    int n;
    bool blank=false;
    while(scanf("%d",&n)!=EOF)
    {
        if(blank) printf("\n");
        else  blank=true;
        memset(dp,,sizeof(dp));
        ; i<=n; ++i)
        {
            scanf("%d",&a[i]);
            ; j<=n; ++j)
                dp[i][j]=;
        }
        has.clear();

        ,x=a[],y;
        ; --i)
        {
            ; j>=; --j)
            {
                hash_map<int,short>::iterator it=has.find(a[i]+a[j]);
                if(it!=has.end())
                {
                    dp[j][i]=dp[i][it->second]+;
                }
                if(ans<dp[j][i])
                {
                    ans=dp[j][i];
                    x=a[j];
                    y=a[i];
                }
            }
            has[a[i]]=i;
        }
        ans++;
        printf("%d\n",ans);
        printf("%d",x);
        ; i<=ans; ++i)
        {
            printf(" %d",y);
            int t=y;
            y=x+y;
            x=t;
        }
        printf("\n");
    }
    ;
}

下面是自己实现的hash。

这个用绝对值取模的hash函数。跑了4s+。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
];
][];
pair<];
static const int mask=0x7fff;
void init()
{
    ; i<=mask; ++i) has[i].first=-;
}
int find(int x)
{
    int key=abs(x)%mask;
    while(has[key].first!=x)
    {
        )
            ;
        key=(key+)%mask;
    }
    return has[key].second;
}
void insert(int x,int val)
{
    int key=abs(x)%mask;
    )
    {
        if(has[key].first==x)
        {
            has[key].second=val;
            return ;
        }
        key=(key+)%mask;
    }
    has[key].first=x;
    has[key].second=val;
}
int main()
{
    int n;
    bool blank=false;
    while(scanf("%d",&n)!=EOF)
    {
        if(blank) printf("\n");
        else  blank=true;
        memset(dp,,sizeof(dp));
        init();
        ; i<=n; ++i)
        {
            scanf("%d",&a[i]);
            ; j<=n; ++j)
                dp[i][j]=;
        }
        ,x=a[],y;
        ; --i)
        {
            ; j>=; --j)
            {
                int it=find(a[i]+a[j]);
                )
                {
                    dp[j][i]=dp[i][it]+;
                }
                if(ans<dp[j][i])
                {
                    ans=dp[j][i];
                    x=a[j];
                    y=a[i];
                }
            }
            insert(a[i],i);
        }
        ans++;
        printf("%d\n",ans);
        printf("%d",x);
        ; i<=ans; ++i)
        {
            printf(" %d",y);
            int t=y;
            y=x+y;
            x=t;
        }
        printf("\n");
    }
    ;
}

这个是用&的hash函数。跑了1s+。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<hash_map>
#include<ext/hash_map>
using namespace std;

];
][];
pair<];
static const int mask=0x7fff;
int Hash(int val)
{
    return val&mask;
}
void init()
{
    ; i<=mask; ++i) has[i].first=-;
}
int find(int x)
{
    int key=x&mask;
    while(has[key].first!=x)
    {
        )
            ;
        key=(key+)&mask;
    }
    return has[key].second;
}
void insert(int x,int val)
{
    int key=x&mask;
    )
    {
        if(has[key].first==x)
        {
            has[key].second=val;
            return ;
        }
        key=(key+)&mask;
    }
    has[key].first=x;
    has[key].second=val;
}
int main()
{
    int n;
    bool blank=false;
    while(scanf("%d",&n)!=EOF)
    {
        if(blank) printf("\n");
        else  blank=true;
        memset(dp,,sizeof(dp));
        init();
        ; i<=n; ++i)
        {
            scanf("%d",&a[i]);
            ; j<=n; ++j)
                dp[i][j]=;
        }
        ,x=a[],y;
        ; --i)
        {
            ; j>=; --j)
            {
                int it=find(a[i]+a[j]);
                )
                {
                    dp[j][i]=dp[i][it]+;
                }
                if(ans<dp[j][i])
                {
                    ans=dp[j][i];
                    x=a[j];
                    y=a[i];
                }
            }
            insert(a[i],i);
        }
        ans++;
        printf("%d\n",ans);
        printf("%d",x);
        ; i<=ans; ++i)
        {
            printf(" %d",y);
            int t=y;
            y=x+y;
            x=t;
        }
        printf("\n");
    }
    ;
}

最后这个是watash写的hash_map。跑了1s+。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<hash_map>
#include<ext/hash_map>
using namespace std;
];
][];
struct Hash
{
    static const int mask = 0x7fff;
    ], q[];
    void clear()
    {
        ; i <= mask; ++i)
            q[i] = -;
    }
    int& operator[](int k)
    {
        int i = k & mask;
         && p[i] != k; i = (i + ) & mask);
        p[i] = k;
        return q[i];
    }
} hash;
int main()
{
    int n;
    bool blank=false;
    while(scanf("%d",&n)!=EOF)
    {
        if(blank) printf("\n");
        else  blank=true;
        memset(dp,,sizeof(dp));
        hash.clear();
        ; i<=n; ++i)
        {
            scanf("%d",&a[i]);
            ; j<=n; ++j)
                dp[i][j]=;
        }
        ,x=a[],y;
        ; --i)
        {
            ; j>=; --j)
            {
                int it=hash[a[i]+a[j]];
                )
                {
                    dp[j][i]=dp[i][it]+;
                }
                if(ans<dp[j][i])
                {
                    ans=dp[j][i];
                    x=a[j];
                    y=a[i];
                }
            }
            hash[a[i]]=i;
        }
        ans++;
        printf("%d\n",ans);
        printf("%d",x);
        ; i<=ans; ++i)
        {
            printf(" %d",y);
            int t=y;
            y=x+y;
            x=t;
        }
        printf("\n");
    }
    ;
}

ZOJ 2672 Fibonacci Subsequence(动态规划+hash)的更多相关文章

  1. ZOJ 2723 Semi-Prime ||ZOJ 2060 Fibonacci Again 水水水!

    两题水题: 1.如果一个数能被分解为两个素数的乘积,则称为Semi-Prime,给你一个数,让你判断是不是Semi-Prime数. 2.定义F(0) = 7, F(1) = 11, F(n) = F( ...

  2. LC 873. Length of Longest Fibonacci Subsequence

    A sequence X_1, X_2, ..., X_n is fibonacci-like if: n >= 3 X_i + X_{i+1} = X_{i+2} for all i + 2 ...

  3. 【LeetCode】873. Length of Longest Fibonacci Subsequence 解题报告(Python)

    [LeetCode]873. Length of Longest Fibonacci Subsequence 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: ...

  4. ZOJ 3349 Special Subsequence

    Special Subsequence Time Limit: 5000ms Memory Limit: 32768KB This problem will be judged on ZJU. Ori ...

  5. HDOJ 1423 Greatest Common Increasing Subsequence -- 动态规划

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1423 Problem Description This is a problem from ZOJ 2 ...

  6. HDU 1159 Common Subsequence 动态规划

    2017-08-06 15:41:04 writer:pprp 刚开始学dp,集训的讲的很难,但是还是得自己看,从简单到难,慢慢来(如果哪里有错误欢迎各位大佬指正) 题意如下: 给两个字符串,找到其中 ...

  7. LeetCode 873. Length of Longest Fibonacci Subsequence

    原题链接在这里:https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/ 题目: A sequence X_1, X ...

  8. 斐波那契数列Fibonacci问题—动态规划

    斐波那契数列定义 Fibonacci array:1,1,2,3,5,8,13,21,34,... 在数学上,斐波那契数列是以递归的方法来定义: F(0) = 0 F(1) = 1 F(n) = F( ...

  9. hdu1159Common Subsequence——动态规划(最长公共子序列(LCS))

    Problem Description A subsequence of a given sequence is the given sequence with some elements (poss ...

随机推荐

  1. linux笔记:linux系统安装-虚拟机网络设置

    vmware虚拟机网络配置的3种方式: 1.桥接:在桥接模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机(主机和虚拟机处于对等地位),它可以访问网内任何一台机器.在桥接模式下,我们 ...

  2. Apache Commons fileUpload实现文件上传之一

      需要两个jar包: commons-fileupload.jar Commons IO的jar包(本文使用commons-io-2.4.jar) 利用Servlet来实现文件上传. package ...

  3. jmeter 建立一个网络服务的测试计划

    如何创建一个 测试计划 测试一个网络服务. 你会 创建5个用户发送请求到一页. 同时,你会告诉用户运行测试两次. 的总数 请求用户请求(5)x(1)x(重复2次)= 10 HTTP请求. 来 建立测试 ...

  4. Linux下修改系统时间并写入BIOS

    我们一般使用“date -s”命令来修改系统时间.比如将系统时间设定成2005年7月26日的命令如下. #date -s 07/26/2005 将系统时间设定成下午11点12分0秒的命令如下. #da ...

  5. NSException

    NSException是什么? 最熟悉的陌生人,这是我对NSException的概述,为什么这么说呢?其实很多开发者接触到NSException的频率非常频繁,但很多人都不知道什么是NSExcepti ...

  6. 在Handler.ashx文件中使用session

    使用jquery调用handler文件中的方法,需要使用session,默认生成的文件中,不可以直接使用session.按照以下步骤,即可以通过session与其他的aspx页面的session进行数 ...

  7. php -l 检查文件是否语法错误

    有时候在进行网页开发的时候,后台文件的语法错误比较难检查出来,这时候使用php -l filename可对文件的语法进行检查.

  8. 在 Visual C# 项目中调用 VBA 中的代码

    https://msdn.microsoft.com/zh-cn/library/Bb608613.aspx http://www.cnblogs.com/yangbin1005/archive/20 ...

  9. MYSQL 日期函数【转】

    MySQL日期时间函数大全 DAYOFWEEK(date) 返回日期date是星期几(=星期六,ODBC标准) mysql> select DAYOFWEEK('1998-02-03'); WE ...

  10. Linux的五个查找命令(find、locate、whereis、which、type)

    1. find find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件. find的使用格式如下: $ find <指定目录> <指定条件> <指定动作> ...