HDU 5693 D Game(区间dp)

来源:互联网 发布:推荐好看的小说知乎 编辑:程序博客网 时间:2024/05/29 15:53

原题链接

题目描述

Problem Description
众所周知,度度熊喜欢的字符只有两个: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
对于每组数据,输出最多能删掉的数字 。

题目分析

第一次看见这个题的时候,笔者也是一头雾水,不知从何下手。回顾题目,对于每组数据,求最多能删除多少数字。看到数组,我们可以自然的想到子数组,求子数组最多能删掉数字是和原问题等价的问题。这个时候,我们能联想到贪心和DP。

“求子数组最多能删掉多少数字?” 是原问题的最优子结构吗?可以试着举举反例

现在,我们来看看动态规划的方法。原问题求数组a[1…N]最多能删掉多少数字?现在,我们考虑字数组a[i…j]最多能删掉多少数字?

dp[l][r] : 从l到r能删掉的最多数字。
c[i][j]: a[i], a[j]构成等差数列(a[j] - a[i] 属于集合D)。
我们采用这样一种思路:看a[l…r]首尾两个数字能否和a[l..r]中间的数字构成等差数列,进而把a[l..r]再拆分成更小的字数组,达到动态转移的目的。

共有以下几种情况:

  1. a[l], a[r]构成等差数列(可以删除),且中间所有的数都能删除。c[l][r] == 1 && dp[l+1][r-1] == r - l - 1;
  2. a[l], a[j](l < j < r)构成等差数列。且两个数中间的数都能删除。c[l][j] == 1 && dp[l+1][j-1] == j - l - 1;
  3. a[r], a[j](l < j < r)构成等差数列。且两个数中间的数都能删除。c[j][r] == 1 && dp[j+1][r-1] == r - j - 1;
  4. a[l], a[j], a[r](l < j < r)构成等差数列。三个数之间的数都能被删除。c[l][j] == 1 && c[j][r] == 1 && a[r] - a[j] == a[j] - a[i] && dp[l+1][j-1] == j - l -1 && dp[j+1][r-1] == r - j -1;

如果不满足上诉四种情况,说明a[l…r]中的a[l], a[r]不能和中间的数建立练习(构成等差数列)。这个时候,我们有:

  1. dp[l][r] == max(dp[l+1][r], dp[l][r-1])
  2. dp[l][r] == max(dp[l][j] + dp[j+1][r]) , l < j < r-1

因为最后是求a[1…N]最多能删掉多少数字, 则不管上述四种情况是否满足,这两种情况都应该计算在内。

AC代码

#include <iostream>#include <cstring>using namespace std;int c[305][305], dp[305][305];int a[305];int main(){    int t, n, m, tmp;    scanf("%d", &t);    while(t--){        memset(dp, 0, sizeof(dp));        memset(c, 0, sizeof(c));        scanf("%d%d", &n, &m);        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);        for(int i = 1; i <= m; i++){            scanf("%d", &tmp);            for(int j = 1; j <= n-1; j++) {                for(int k = j+1; k <= n; k++) {                    if(a[k] - a[j] == tmp) c[j][k] = 1;                }            }        }        for(int k = 1; k <= n-1; ++k) {            for(int l = 1; l+k <= n; l++) {                int r = l+k;                dp[l][r] = max(dp[l+1][r], dp[l][r-1]);                if(c[l][r] == 1 && dp[l+1][r-1] == r-l-1) {                    dp[l][r] = max(dp[l][r], dp[l+1][r-1]+2);                }                for(int j = l+1; j <= r-1; j++) {                    dp[l][r] = max(dp[l][r], dp[l][j]+dp[j+1][r]);                    if(c[l][j] == 1 && dp[l+1][j-1] == j-l-1)                        dp[l][r] = max(dp[l][r], dp[l+1][j-1]+dp[j+1][r]+2);                    if(c[j][r] == 1 && dp[j+1][r-1] == r-j-1)                        dp[l][r] = max(dp[l][r], dp[l][j-1]+dp[j+1][r-1]+2);                    if(c[l][j] == 1 && c[j][r] == 1 && a[r] - a[j] == a[j] - a[l] && dp[l+1][j-1] == j-l-1 && dp[j+1][r-1] == r-j-1)                        dp[l][r] = r-l+1;                }            }        }        cout << dp[1][n] << endl;    }    return 0;}
原创粉丝点击