P1868 饥饿的奶牛(线性dp,数值dp)
题目描述:
有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。
现用汉语翻译为:
有 NN 个区间,每个区间 x,yx,y 表示提供的 x\sim yx∼y 共 y-x+1y−x+1 堆优质牧草。你可以选择任意区间但不能有重复的部分。
对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。
输入格式
第一行一个整数 NN。
接下来 NN 行,每行两个数 x,yx,y,描述一个区间。
输出格式
输出最多能吃到的牧草堆数。
输入输出样例
输入 #1
3 1 3 7 8 3 4
输出 #1
5
说明/提示
1 \leq n \leq 1.5 \times 10^51≤n≤1.5×105,0 \leq x \leq y \leq 3 \times 10^60≤x≤y≤3×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; }