POJ 3670 Eating Together

之前一直不过,最后的过的思路如下:

dp[i][j]表示的含义是  到第i位,最高为j的子序列的最长长度。

递推式:

dp[i][1] = dp[i-1][1] + (a[i] == 1?1:0);
dp[i][2] = max(dp[i][1], dp[i-1][2]) + (a[i]==2?1:0);
dp[i][3] = max3(dp[i][1], dp[i][2], dp[i-1][3]) + (a[i]==3?1:0)

高度为1的,最长只能为之前的,加上本身(如果本身是1则可加,否则不行)(1111_)

高度为2的,为刚才算出的 高1序列最长 与 之前高2序列最长 的最大值 再视情况包含本身。(11111_   或者 11112_)

高度为3类同。(11111_ 或者 1111222_ 或者 11112222333_)

为什么要算高2时要算 第i位高1 而不是第i-1位呢? 因为。如果当i是1的时候,则dp[i][1]>dp[i-1][1], 同时,dp[i][1]也符合dp[i][2](只是要求最高为2, 不是一定要到2,所以序列111也可以包含在高2的序列内),所以应当选dp[i][1];

那么代码是如何避免 高3 序列包含如 1232123 这样不合法的序列呢?

注意到,每个状态只能选择合并较低状态,或者保持之前状态,或者包含与本状态相当的数(如高2状态只会包含2),所以比本状态小的数在之后是不会进入这个状态序列的。

注意到,当a[i]=2时,原先的高1保持不变,包含a[i]这位的子序列的只可能 加上dp[i][1]后包含在 高2 的状态内。(也可能不包含a[i],情况就是dp[i-1][2]比较大)而高2状态中不可能继续包含1.

同样,a[i]=3时,包含该位的子序列也就跳入了 高3 中,之后高3只可能继续包含3 而不可能包含1 2, 所以不会出现1231 或者 1232这样的情况,只可能是1233.

……还是没说清楚。。

至于为什么是可行的……

  i位前高j子序列是什么样的对i位以后没影响。所以任意都可以,自然是选最长的。。。

POJ 3670 Eating Together
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define max(a,b) ((a)>(b)?(a):(b))
#define max3(a, b, c) (max((max((a),(b))),(c)))

int dp[30010][4];
int a[30010], b[30010];
int n;

int cal(int a[]) {
    int i;
    dp[0][1] = a[0] == 1?1:0;
    dp[0][2] = dp[0][1] + a[0] == 2?1:0;
    dp[0][3] = (max(dp[0][1], dp[0][2])) + (a[0]==3?1:0);

    for (i = 1; i < n; i++) {
        dp[i][1] = dp[i-1][1] + (a[i] == 1?1:0);
        dp[i][2] = max(dp[i][1], dp[i-1][2]) + (a[i]==2?1:0);
        dp[i][3] = max3(dp[i][1], dp[i][2], dp[i-1][3]) + (a[i]==3?1:0);
    }

    int max = 0;
    for (i = 1; i <= 3; i++) {
        if (max < dp[n-1][i]) max = dp[n-1][i];
    }
    return max;
}

int main() {
    while (~scanf("%d", &n)) {
        int i;
        for (i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            b[n-1-i] = a[i];
        }
        int max = cal(a);
        int temp = cal(b);
        if (max < temp) max = temp;
        printf("%d\n", n - max);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shinecheng/p/3068724.html