POJ 1722: SUBTRACT

题形:DP,背包

题意:有一个数组,里面保存着n个数。con(i) 操作表示 把a[i] 和 a[i+1] 这两项 用 a[i]-a[i+1]这一项取代(所以操作结束后数组长度会减少1)。通过n-1次操作,可以让数组只剩下一个数。给你一个目标数,求n-1次操作,使得经过这些操作之后这个数组能变成这个目标数(题目保证存在解)

思路:

题目拐了个大弯啊~隐藏的真好。需要转换模型。

首先,容易把题意转变成,a[1]-a[2]-a[3]-...-a[n],这样一个算式,让你加括号,使得结果为目标数

然后想象一下最后的答案。假设我们知道最后的答案,那么把所有括号拆掉,结果的式子一定一这样的:a[1]-a[2]±a[3]±a[4]±...±a[n] = 目标数

再转。把前两个符号确定的移走,就成了±a[3]±a[4]±...±a[n] = 目标数-a[1]+a[2].

左边已经很像背包了,要么正,要么负。如果变成,要么有,要么没有,那就完全是背包了。怎么变呢?

2*a[3]  2*a[4]  2*a[5] ... 2*a[n]  = 目标数-a[1]+a[2] + (a[3]+a[4]+a[5]+...+a[n])

这样,对于左边的某个2*a[i],如果取,那么就表示这个数原来的符号是+,如果不取,就是-。

最后是还原回操作。本来以为不可操作,实际挺简单的。

观察:

a-b+c+d-e+f-g

= a-(b-c-d) - (e-f) - g

= a-((b-c)-d) - (e-f) - g

也就是说,从左往右扫过来,如果数a[i]的符号为正,则让它与左边的数合并。最后,所有的数都和1合并。就行了。

这里需要注意,合并以后造成的坐标变化。我的实现是:

re[i] 保存第i个数在最优解中是否被取

stk[i] 保存所有被取的数(从左到右)

则:

        for (int i = 0; i < n-2; i++) {
            if (re[i] == 1) st[top++] = i+2;//+2纯为数出来的。反正是差个系数
        }
        for (int i = 0; i < top; i++) {
            st[i] -= i; //前面已经合并了i次,所以少了i个
        }

代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#define V 30000
#define N 200

int c[N];
int dp[V];
int record[N][V];
int re[N];
int st[N];

int main() {
    int n,t;
    while (~scanf("%d%d", &n, &t)) {
        int a, b;
        if (n == 1) {
            scanf("%d", &a);
            continue;
        }
        if (n == 2) {
            scanf("%d%d", &a, &b);
            printf("%d\n", 1);
            continue;
        }

        scanf("%d%d", &a, &b);
        int sum = 0;
        for (int i = 0; i < n-2; i++) {
            scanf("%d", &c[i]);
            sum += c[i];
            c[i] *= 2;
        }

        int v = sum-a+b+t;
        //assert v > 0

        for (int i = 0; i <= v; i++) {
            dp[i] = 0;
        }
        for (int i = 0; i < n-2;i++) {
            for (int j = v; j - c[i] >= 0; j--) {
                if (dp[j] > dp[j-c[i]]+c[i]) {
                    record[i][j] = 0;
                } else {
                    dp[j] = dp[j-c[i]]+c[i];
                    record[i][j] = 1;
                }
            }
        }
        //printf("dp = %d(v= %d)\n", dp[v], v);
        int nowv = v;
        for (int i = n-2-1; i >= 0; i--) {
            if (record[i][nowv] == 1) {
                re[i] = 1;
                nowv -= c[i];
            } else re[i] = 0;
        }

        int top = 0;
        for (int i = 0; i < n-2; i++) {
            if (re[i] == 1) st[top++] = i+2;
        }
        for (int i = 0; i < top; i++) {
            st[i] -= i;
        }
        for (int i = 0; i < top; i++) {
            printf("%d\n", st[i]);
        }
        for (int i = top; i < n-1; i++) {
            puts("1");
        }
    }
    return 0;
}

 

 

 

posted on 2014-03-06 18:41  ShineCheng  阅读(406)  评论(0编辑  收藏  举报

导航