首页 > 代码库 > poj - 1722 - SUBTRACT(dp)

poj - 1722 - SUBTRACT(dp)

题意:一个长为 N (1 <= N <= 100)的序列(1 <= ai <= 100),一次操作为删除 a[i] 和 a[i + 1],然后将它们的差(a[i] - a[i + 1])放入该位置,问 N - 1 次操作后得到 T (-10000 <= T <= 10000)的操作顺序是什么?

题目链接:http://poj.org/problem?id=1722

——>>每次操作相当于给 a[i] 和 a[i + 1] 加括号做减法,那么把所有的括号去掉后就是对序列第一次做减法,后面或加法或减法。。

状态:dp[i][j] 表示前 i 个数的运算结果为 j 时最后一次的运算符号。。("+" 或 "-")

状态转移方程:

dp[i + 1][j - a[i + 1]] = ‘-‘;

dp[i + 1][j + a[i + 1]] = ‘+‘;

#include <cstdio>
#include <cstring>

const int MAXN = 100 + 10;
const int MAXT = 20000 + 10;
const int MID = 10000;

int N, T;
int a[MAXN];
char dp[MAXN][MAXT];
char op[MAXN];

void Read()
{
    for (int i = 1; i <= N; ++i)
    {
        scanf("%d", a + i);
    }
}

void Dp()
{
    if (N == 1) return;

    memset(dp, 0, sizeof(dp));
    dp[2][a[1] - a[2] + MID] = '-';
    for (int i = 2; i < N; ++i)
    {
        for (int j = 0; j < (MID << 1); ++j)
        {
            if (dp[i][j] != 0)
            {
                dp[i + 1][j - a[i + 1]] = '-';
                dp[i + 1][j + a[i + 1]] = '+';
            }
        }
    }
}

void GetPath()
{
    int t = T + MID;
    for (int i = N; i >= 2; --i)
    {
        op[i] = dp[i][t];
        if (op[i] == '+')
        {
            t -= a[i];
        }
        else
        {
            t += a[i];
        }
    }
}

void Output()
{
    int done = 0;
    for (int i = 2; i <= N; ++i)
    {
        if (op[i] == '+')
        {
            printf("%d\n", i - 1 - done);
            ++done;
        }
    }

    for (int i = 2; i <= N; ++i)
    {
        if (op[i] == '-')
        {
            puts("1");
        }
    }
}

int main()
{
    while (scanf("%d%d", &N, &T) == 2)
    {
        Read();
        Dp();
        GetPath();
        Output();
    }

    return 0;
}


poj - 1722 - SUBTRACT(dp)