HUD-5693 D Game(区间DP+分类讨论)

D Game

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1148    Accepted Submission(s): 424


 

Problem Description

众所周知,度度熊喜欢的字符只有两个:B 和D。

今天,它发明了一个游戏:D游戏。

度度熊的英文并不是很高明,所以这里的D,没什么高深的含义,只是代指等差数列[(等差数列百科)](http://baike.baidu.com/view/62268.htm)中的公差D。

这个游戏是这样的,首先度度熊拥有一个公差集合{D} ,然后它依次写下N 个数字排成一行。游戏规则很简单:

1. 在当前剩下的有序数组中选择X(X≥2) 个连续数字;

2. 检查1 选择的X 个数字是否构成等差数列,且公差 d∈{D} ;

3. 如果2 满足,可以在数组中删除这X 个数字;

4. 重复 1−3 步,直到无法删除更多数字。

度度熊最多能删掉多少个数字,如果它足够聪明的话?

 

Input

第一行一个整数T ,表示T(1≤T≤100) 组数据。

每组数据以两个整数 N ,M 开始 。接着的一行包括 N 个整数,表示排成一行的有序数组 Ai 。接下来的一行是 M 个整数,即给定的公差集合 Di 。

1≤N,M≤300

−1 000 000 000≤Ai,Di≤1 000 000 000

 

Output

对于每组数据,输出最多能删掉的数字 。

 

Sample Input

 

3 3 1 1 2 3 1 3 2 1 2 4 1 2 4 2 1 3 4 3 1 2

 

Sample Output

 

3 2 4

 

Source

2016"百度之星" - 初赛(Astar Round2A)

 

Recommend

wange2014   |   We have carefully selected several similar problems for you:  6361 6360 6359 6358 6357 

  区间dp:子区间最优推出整个区间最优。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#include<cstring>
using namespace std;
int dp[310][310];   //dp[i][j]表示i~j这个区间的最大可删除的数字
int n,t,m,d[310];   //基本思想:>=2的数都可以由2和3组成,每次至少删除2个区间或3个区间
                    //分类讨论
map<int,bool> vis;  //md因为这是下标法,等差d<=10^9,开数组的话就爆了,用map离散化,省空间

int a[310];
int main()
{
    int i,k;
    int l,r,len;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
        for(i=1;i<=m;i++)
        {
           scanf("%d",&d[i]);
           vis[d[i]]=1;
        }
        for(i=1;i<n;i++)
        //一.预处理所有长度为2和3的区间
        //   1.直接找两个等差数列的区间
        if(vis[a[i+1]-a[i]])
        {
            dp[i][i+1]=2;
        }
        //  2.找三个等差数列的区间
        for(i=1;i<n-1;i++)
        {
            if(a[i]+a[i+2]==2*a[i+1]&&vis[a[i+1]-a[i]])  //额,a[i]+a[i+2]==2*a[i+1]不就是等差数列的性质
            dp[i][i+2]=3;                                //只要一个vis,因为只要一个d啊
            else
            dp[i][i+2]=max(dp[i][i+1],dp[i+1][i+2]);     //这也很自然
        }
        //找四个以上等差序列(包括四个)
        //由长度为2和3的区间组成
        //组成的思想:
        //原本l~r这个区间并不一定是等差的
        //形成两种情况:
        //1. l~r都等差
        //2. l~r删掉一部分等差,剩下的部分等差。
        // 这两种情况的处理方法一样
        for(len=4;len<=n;len++)        //处理区间DP的两个for,长度,左,推出右
        for(l=1;l+len-1<=n;l++)                          //len-1因为要去掉l这个长度。
        {
            r=l+len-1;                                   //为什么做这下面的讨论
                                                         //基本思想:>=2的数都可以由2和3组成
                                                         //要删掉一部分等差,这部分等差的长度一定是2或3
                                                         //长度又是>=4的
                                                         //以长度为4,5为特例展开讨论
                                                         //形成以下两种情况
                                                         //左右各成为等差序列,中间是等差序列
            if(dp[l+1][r-1]==len-2&&vis[a[r]-a[l]])
            dp[l][r]=len;
            if(dp[l+1][r-2]==len-3&&vis[a[r]-a[r-1]]&&a[l]+a[r]==2*a[r-1])   //中间是等差序列,左右两端可以拼成等差序列
            dp[l][r]=len;
            if(dp[l+2][r-1]==len-3&&vis[a[l+1]-a[l]]&&a[l]+a[r]==2*a[l+1])
            dp[l][r]=len;
            for(k=l;k<r;k++)                             //区间长度为4只进行第一个if 区间长度>=5三个if
                                                         //区间为4左取两个,右取两个,可用区间dp,dp出来所以不需要讨论
                                                         //想要再次强调区间dp的k<r
            dp[l][r]=max(dp[l][k]+dp[k+1][r],dp[l][r]);  
        }
        printf("%d\n",dp[1][n]);
        for(i=1;i<=m;i++)
        vis[d[i]]=0;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/81483299