?参考博客:寒假思维训练day10 浅谈状态机DP-CSDN博客
??1、前后缀贪心,比如说观察前后缀的sum,去看以后怎么考虑最好。Problem - 1903C - Codeforces
2、双指针贪心法,考虑两端相消或者相互作用,还有就是考虑左右边界。? ?Problem - 1891C - Codeforces
3、转换观察法,有些关系可以抽象成图,观察图的某些性质去总结规律。也可以抽象成一个集合,两个集合相等可以说明有解可构造。Problem - 1891C - Codeforces
4、打表找规律,一般没什么规律可循即可打表找规律,一般和数论有关的很喜欢考,acm也喜欢考,属于人类智慧题。Problem - 1916D - Codeforces
5、公式推导演算,常见的分为公式的等价变形、公式的化简(这个常考,一般需要先证明某些性质,可以直接抵消,一般如果原公式处理起来很复杂时就可以考虑)。Problem - 1889B - Codeforces
6、考虑奇偶数去简化问题或者分类问题,从其中的一些运算性质入手,因为奇数偶数的加减以及%运算(这个结论很重要)的结果的奇偶性是固定的,Problem - 1898C - Codeforces
7、根据性质构造模型,看看能不能分成几个块,几个不同的集合,再选择算法去解决。Problem - 1873G - Codeforces
8、考虑从小到大处理,或者是从大到小处理,有时候先处理小的对大的不会有影响,或者反过来,这样的处理顺序是最完美的。Problem - 1904D2 - Codeforces
9、边界贪心法,一般要在问题的最边界处考虑,有时候这样做结果是最优的,或者考虑边界上的影响,假如让影响最小,就使得影响<= 固定值 。???????Problem - E - Codeforces?and?Problem - 1903C - Codeforces
(1)、题意:沿着道路有n棵树,它们的坐标分别为x1,?x2,?...,?xn。每棵树都有它的高度h[i]。伐木工人可以砍倒一棵树并使其倒向左或右。之后,树将占据区间[xi?-?hi,?xi]或[xi, xi?+?hi]之一(闭区间)。没有被砍倒的树将占据坐标xi的一个点。伐木工人可以砍倒一棵树,前提是树倒下后占据的区间不包含任何被占据的点,问最多可以砍多少棵树。
(2)、题解:显然这道题的情况非常多,很难去贪心,这种情况不妨多往图论、DP方面去多想想,现在我们考虑一下如何用状态机DP去解决这个问题:
(3)、DP求解:方程定义:DP[i][0], 将第i棵树砍到往左,DP[i][1] 不砍第i棵树,DP[i][2] 砍第i颗树往右边。
方程转移:
我们先要处理好两个边界,假设x[0]是负无穷,x[n + 1]是正无穷,它们的h[i] = 0,并且x[i], h[i] >= 1,默认1到n升序排列,这样子就可以探讨了,从前往后考虑(py伪代码):考虑dP[i][0], 也就是砍倒第i颗树往右倒: if a[i] - b[i] > a[i - 1] + b[i - 1]: dp[i][0] = max(dp[i - 1][1], dp[i - 1][0], dp[i - 1][2]) + 1 else: if a[i] - b[i] > a[i - 1]: dp[i][0] = max(dp[i - 1][1], d[i - 1][0]) + 1 else: dp[i][0] = -1e18 (无解) 考虑dp[i][1], 也就是不砍第i颗树: dp[i][1] = max(dp[i - 1][1], dp[i - 1][0], dp[i - 1][2]) 考虑dp[i][2], 也就是砍到第i颗树向右: if a[i] + b[i] < a[i + 1]: dp[i][2] = max(dp[i - 1][1], dp[i - 1][0], dp[i - 1][2]) + 1 else: dp[i][2] = -1e18(无解)
(4)、答案代码:
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e5 + 10; int n, m; int a[N], b[N]; int f[N][3]; // f[i][0] 左 // f[i][1] 不放 // f[i][2] 右 int max(int x, int y, int z) { return max(max(x, y), z); } void solve() { cin >> n; a[0] = -1e18, a[n + 1] = 1e18; for(int i = 1; i <= n; i ++ ) cin >> a[i] >> b[i]; for(int i = 1; i <= n; i ++ ) { // f[i][0] 往左 if(a[i] - b[i] > a[i - 1] + b[i - 1]) f[i][0] = max(f[i - 1][1], f[i - 1][0], f[i - 1][2]) + 1; else { if(a[i] - b[i] > a[i - 1]) f[i][0] = max(f[i - 1][1], f[i - 1][0]) + 1; else f[i][0] = -1e18; } // f[i][1] 不动 f[i][1] = max(f[i - 1][1], f[i - 1][0], f[i - 1][2]); // f[i][2] 往右 if(a[i] + b[i] < a[i + 1]) f[i][2] = max(f[i - 1][1], f[i - 1][0], f[i - 1][2]) + 1; else f[i][2] = -1e18; } cout << max(0ll, max(f[n][1], f[n][0], f[n][2])) << endl; } signed main() { int ts = 1; // cin >> ts; while(ts --) solve(); return 0; }