P1868 饥饿的奶牛(线性dp,数值dp)

题目描述:

有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。

现用汉语翻译为:

有 NN 个区间,每个区间 x,yx,y 表示提供的 x\sim yxy 共 y-x+1yx+1 堆优质牧草。你可以选择任意区间但不能有重复的部分。

对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

输入格式

第一行一个整数 NN。

接下来 NN 行,每行两个数 x,yx,y,描述一个区间。

输出格式

输出最多能吃到的牧草堆数。

输入输出样例

输入 #1
3
1 3
7 8
3 4
输出 #1
5

说明/提示

1 \leq n \leq 1.5 \times 10^51n1.5×105,0 \leq x \leq y \leq 3 \times 10^60xy3×106。

思路:一眼看过去就是很熟悉的感觉,首先对区间按右边界排序,然后从左到右线性dp,代码如下:

dp[i]表示以i区间结尾的最大覆盖值

#include<bits/stdc++.h>
using namespace std;
const int maxn = 150005;
int n;
struct node {
    int a, b;
    bool operator<(node c)const {
        return b < c.b;
    }
}v[maxn];
int dp[maxn];
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &v[i].a, &v[i].b);
    }
    sort(v + 1, v + n + 1);
    dp[1] = v[1].b - v[1].a+1;
    int ans = dp[1];
    for (int i = 2; i <= n; i++) {
        dp[i] = v[i].b - v[i].a+1;
        for (int j = i - 1; j >= 1; j--) {
            if (v[j].b < v[i].a) {//如果区间i的左区间大于区间j的右区间,则可以满足
                dp[i] = max(dp[i], dp[j] + v[i].b - v[i].a+1);
            }
        }
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;
    return 0;
}

复杂度O(n^2),会超时

我们再仔细观察数据,会发现xy取值很小,所以我们理所当然的想到数值dp最优解的方法,即以某个位置为结束位置的最大解来dp

dp[i]不再表示以i区间结尾,而是以i为界限,在它之前的所有区间能组成的最大值

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3000005;
int n;
struct node {
    int a, b;
    bool operator<(node c)const {
        return b < c.b;
    }
}v[maxn];
int dp[maxn];
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d", &n);
    int r = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &v[i].a, &v[i].b);
        r = max(r, v[i].b);//求出右区间
    }
    sort(v + 1, v + n + 1);
    int i = 1;
    for (int s = 1; s <= r; s++) {//端点不断向右修改
        dp[s] = dp[s - 1];//不做任何选择
        while(s == v[i].b) {//如果当前端点是右区间,进行选择
            if (v[i].a == 0)dp[s] = v[i].b + 1;//这里分两次判断是为了防止a[i]=0的情况导致数组越界
            else dp[s] = max(dp[s], dp[v[i].a - 1] + v[i].b - v[i].a + 1);
            i++;
        }
    }
    cout << dp[r] << endl;
    return 0;
}

 

posted @ 2021-03-20 21:07  cono奇犽哒  阅读(76)  评论(0)    收藏  举报