CF998B Cutting 题解 DP+贪心

Posted quanjun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF998B Cutting 题解 DP+贪心相关的知识,希望对你有一定的参考价值。

题目链接:http://codeforces.com/problemset/problem/998/B

题目描述

有很多东西是可以被切割的,比如——树、纸张或者绳子。在这道题目里面你需要切割一个整数序列。
现在告诉你一个整数序列,在这个整数序列里面有一些数,它们可能是奇数,也可能是偶数。
给你一个固定的预算(因为切割是有成本的),你需要尽可能多地将这个整数序列切分成一系列连续子序列,
使得每一个连续子序列中的 奇数元素的个数 和 偶数元素的个数 相同。
比如,给你一个整数序列 [4,1,2,3,4,5,4,4,5,5] ,你可以将其切割两次变成
[4,1 | 2,3,4,5 | 4,4,5,5] 。其中,[4,1] 、 [2,3,4,5] 以及 [4,4,5,5] 这三个连续子序列中包含的奇数个数等于偶数个数。
如果你要将第 i 个元素和第 i+1 个元素之间切一刀,我们假设第 i 个元素对应的数值为 x ,第 j 个元素对应的数值为 y ,那么你需要消耗 |x-y| 个比特币。
(|x-y| 表示 x-y 的差的绝对值)。
而你的预算只有 B 个比特币,所以你需要计算一下在最多消耗 B 个比特币的情况下,你最多可以切几次。

输入格式

输入的第一行包含两个整数 n (2<=n<=100)和 B(1<=B<=100),分别表示整数序列中元素的个数和你最多可用的比特币的数量。
输入的第二行包含 n 个整数,用于表示整数序列中的元素:a1,a2,……,an(1<=ai<=100)。

输出格式

输出一个整数,用于表示在最多消耗 B 个比特币的情况下,你最多可以切几刀,使得每一个切出来的连续子序列中包含相同的奇偶元素。

样例输入1

6 4
1 2 5 10 15 20

样例输出1

1

样例输入2

4 10
1 3 2 4

样例输出2

0

样例输入3

6 100
1 2 3 4 5 6

样例输出3

2

样例解释

对于样例1,我们可以在 2 和 5 之间切割一刀,消耗 3 个比特币;
对于样例2,我们无法切割;
对于样例3,我们可以在 2 和 3 之间 以及 4 和 5 之间切割一刀,消耗 1+1=2 个比特币。

问题分析

这道题目涉及的算法是“DP”+“贪心”,可以用 dp+sort 或者 dp+heap 来做。
首先我们假设 n 个元素的坐标从 1 到 n ,他们分别对应 a[1] 到 a[n] 。
然后我们开一个输出 cc[] ,cc[i] 用于表示从 a[1] 到 a[i] 这 i 个数中奇数个数减去偶数个数之差。
那么对于 1 到 n-1 范围内的 i ,如果 cc[i] == 0 ,那么它这个位置就是可以切割的,切割的成本是 abs(a[i+1]-a[i]) 。

dp+sort解法:

我们将 1 到 n-1 范围内的所有满足 cc[i]==0 条件的 i 对应的 abs(a[i+1]-a[i]) 放入一个数组中,然后将这个数组从小到大排序,每次取出一个数计数器cnt++,同时B减去这个数,直到B不够用为止;

dp+heap(堆)解法:

我们首先构造一个最小堆(可以用 priority_queue 模拟),然后将 1 到 n-1 范围内的所有满足 cc[i]==0 条件的 i 对应的 abs(a[i+1]-a[i]) 放入最小堆中1,然后每次从最小堆中取出堆顶元素,计数器cnt++,同时B减去这个数,直到B不够用为止。

1、下面的代码演示了 dp+sort 的操作:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;

int n, B, cnt, a[maxn], cc[maxn];
vector<int> vec;

int main() {
    cin >> n >> B;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        cc[i] = cc[i-1] + (a[i] % 2 ? 1 : -1);
    }
    for (int i = 1; i < n; i ++) if (!cc[i]) vec.push_back(abs(a[i+1]-a[i]));
    sort(vec.begin(), vec.end());
    for (vector<int>::iterator it = vec.begin(); it != vec.end(); it ++) {
        if (*it > B) break;
        B -= *it;
        cnt ++;
    }
    cout << cnt << endl;
    return 0;
}

2、下面的代码演示了 dp+heap 的操作:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;

int n, B, cnt, a[maxn], cc[maxn];
priority_queue<int, vector<int>, greater<int> > que;

int main() {
    cin >> n >> B;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        cc[i] = cc[i-1] + (a[i] % 2 ? 1 : -1);
    }
    for (int i = 1; i < n; i ++) if (!cc[i]) que.push(abs(a[i+1]-a[i]));
    while (!que.empty()) {
        int u = que.top();
        que.pop();
        if (u > B) break;
        B -= u;
        cnt ++;
    }
    cout << cnt << endl;
    return 0;
}

以上是关于CF998B Cutting 题解 DP+贪心的主要内容,如果未能解决你的问题,请参考以下文章

[CF594E]Cutting the Line

[CF571B]Minimization(贪心+DP,好)

题解CF1133E K Balanced Teams

hdu5909-Tree Cutting(树形dp)

CF981D Bookshelves

Cutting Sticks UVA - 10003