[多阶段DP] 洛谷P1282 多米诺骨牌 (01背包)

题目

LP1282

思路

本题总体不难。。甚至我觉得难度评级应该是普及+/提高。。。但就是做了一上午。。
自己要蠢死啦。。


大体思路是这样的,思考以后就会发现,本题其实是一个纠正差值的问题,拿这样样例举例:

4
6 1
1 5
1 3
1 2

可以看到,上面点数之和为9,下面点数之和为11,差值为2(暂不考虑正负)。
而每张牌,都有自己的“可修正差值数”,比如第一张牌就是10(上面减5下面加5)。样例的情况,用最后一张牌,可修正数差值数为2,直接用它一张就可以了。


那么我们需要找到这个问题的普遍解决方法:
1.计算出差值C,当上面点数较大时,C为正。
2.计算出每张牌的“可修正差值数”A[i],当翻过来以后上面点数减少时,A[i]为正。
3.用A[1…n]凑出差值C,要求所用到牌数最少,输出最少牌数即可。


实现方法:
1~2.直接算即可,2计算时不要忘了乘2。
3.此处使用01背包

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = 1 << 28;
const int maxn = 1000 + 10;
const int maxs = 10000;
int n, C, A[maxn], d[maxn][2 * maxs];  //C为差值

int main() {
    scanf("%d", &n);
    int a, b, S1 = 0, S2 = 0;
    _rep(i, 1, n) {
        scanf("%d%d", &a, &b);
        A[i] = (a - b) * 2;
        S1 += a, S2 += b, C += (a - b);   // max(S1,S2)为理论上,需要凑的数的最大值
    }

    _rep(i, 0, n)  // 因为j可能为负,为了防止访问负下标,所以使用maxs
        for (int j = maxs - max(S1, S2); j < maxs + max(S1, S2); j++) {
            // 相当于d的初始化操作
            if (i == 0) {
                if (j == maxs) d[0][j] = 0;
                else d[0][j] = INF;
                continue;
            }

            d[i][j] = d[i - 1][j];
            if (j - A[i] >= maxs - max(S1, S2) && j - A[i] < maxs + max(S1, S2))
                d[i][j] = min(d[i][j], d[i - 1][j - A[i]] + 1);
        }

    int ans;   // 达到点数之差最小的时候
    _rep(i, 0, n) {
        ans = d[n][C + maxs + i - 1];
        if (ans <= 1000) break;
        ans = d[n][C + maxs - i - 1];
        if (ans <= 1000) break;
    }
    printf("%d\n", ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/80969074