D Game HDU - 5693(区间DP)

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

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

度度熊的英文并不是很高明,所以这里的D,没什么高深的含义,只是代指等差数列(等差数列百科)中的公差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

思路:
区间dp
如果定义状态F[i][j]代表区间可以删掉多少个数,明显就不好转移了。
但如果定义的是可行性方案,F[i][j]代表区间i到j能否全部删掉,那就好办了。

一个显然的结论是:对于删除的数,都可以拆成删除2个和删除3个。

当区间长度为1的时候,肯定不能删除。区间长度为2和3的时候单独讨论。
那么对于区间[i,j]。有两种讨论的情况。
1.i和j已经被删除:那么此种情况下F[i][j] = F[i][k] & F[k + 1][j]
2.i和j还没有被删除:此时分为删除2个数和删除3个数。删除两个数就是单独判断a[j] - a[i]和F[i + 1][j - 1]。删除3个数就是F[i + 1][k - 1] & F[k + 1][j - 1],并且讨论a[i] a[k] a[j]

讨论出了每个区间能否被删除,再定义f[i]代表前i个数能被删除的最大数目是多少。
f[i] = max(f[i - 1],f[j - 1] + i - j + 1)
n2可以算出 f 数组。

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>

using namespace std;

typedef long long ll;
map<int,int>mp;
int can[305][305];
int f[305],a[305];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        mp.clear();
        memset(can,0,sizeof(can));memset(f,0,sizeof(f));
        int n,m;scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)scanf("%d",&a[i]);
        for(int i = 1,x;i <= m;i++)scanf("%d",&x),mp[x] = 1;
        
        for(int len = 2;len <= n;len++)
        {
            for(int i = 1;i + len - 1 <= n;i++)
            {
                int j = i + len - 1;
                if(len == 2)
                {
                    if(mp[a[j] - a[i]])can[i][j] = 1;
                    continue;
                }
                else if(len == 3)
                {
                    int k = j - 1;
                    if(a[j] - a[k] == a[k] - a[i] && mp[a[j] - a[k]])can[i][j] = 1;
                    continue;
                }
                
                for(int k = i + 1;k < j - 1;k++)
                {
                    if(can[i][k] && can[k + 1][j])can[i][j] = 1;
                }
                
                if(can[i + 1][j - 1] && mp[a[j] - a[i]])can[i][j] = 1;
                for(int k = i + 3;k < j - 2;k++)
                {
                    if(can[i + 1][k - 1] && can[k + 1][j - 1])
                    {
                        if(a[j] - a[k] == a[k] - a[i] && mp[a[j] - a[k]])
                        {
                            can[i][j] = 1;
                        }
                    }
                }
            }
        }
        
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j < i;j++)
            {
                f[i] = max(f[i],f[i - 1]);
                if(can[j][i])f[i] = max(f[i],f[j - 1] + i - j + 1);
            }
        }
        printf("%d\n",f[n]);
    }
    return 0;
}

发布了676 篇原创文章 · 获赞 18 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/104213275