题解 P1868 【饥饿的奶牛】

zhy137036

2020-02-28 20:20:45

Solution

这篇题解记录了我做这道题的思考过程。 ## 题意简述 - 给你 $n$ 个区间 $(x,y)$,长度为 $y-x+1$。 - 选择不相交的几个区间,使长度之和最大。 - 两个区间 $(x_1,y_1)$ 和 $(x_2,y_2)$ 不相交,当且仅当 $y_1<x_2$ 或 $y_2<x_1$。 - $1\le x\le1.5\times10^5$,$0\le x\le y\le3\times10^6$。 ## 题目分析 有人曾经说过:你不会的题很可能就是动态规划。 这道题就是动态规划。 区间?感觉是线性动态规划,再看数据范围,没错了。 ### 状态定义 首先不妨想象这些草地是一个一个的格子。 先从最简单的定义想起: 设 $f_i$ 表示前 $i$ 个格子最多能吃到多少牧草。 虽然这个定义很简单,但是很好用。 ### 转移式 显然 $f_j\ge f_{j-1}$。多一个格子选择,吃得总不会更少。 如果有一个区间 $(i,j)$,那么 $f_j\ge f_{i-1}+j-i+1$。 最后得到转移式: $$\large f_j=\max(f_{j-1},\max f_{i-1}+j-(i-1))$$ ### 细节处理 但是怎么对于所有的 $j$ 都知道对应的所有的 $i$ 呢? 发现一个 $j$ 所对应的 $i$ 的个数是不固定的,所以想到用 `std::vector<int>` 保存。 ## 代码 自认为代码比较简洁。 ```cpp #include<cstdio> #include<algorithm> #include<vector> using namespace std; vector<int>beg[3000010];//有点大,不过并不会 MLE int n,mx,f[3000010];//mx 代表最大的 y,f 就是 dp 用的数组 int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); beg[y].push_back(x-1);//这里保存的是 x-1,后面会比较方便 mx=max(mx,y); } for(int i=1;i<=mx;i++){ f[i]=f[i-1];//先设定为 f[i-1],后面再更新 for(int j=0;j<beg[i].size();j++){ int b=beg[i][j]; f[i]=max(f[i],f[b]+i-b);//这里会比较方便 } } printf("%d\n",f[mx]); return 0; } ```