寒假思维训练day10

 参考博客:寒假思维训练day10 浅谈状态机DP-CSDN博客

宣传一下我总结的几个构造题模型,一点个人的浅薄见解:

  1、前后缀贪心,比如说观察前后缀的sum,去看以后怎么考虑最好。Problem - 1903C - Codeforces

2、双指针贪心法,考虑两端相消或者相互作用,还有就是考虑左右边界。   Problem - 1891C - Codeforces

Problem - 1907D - 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


寒假思维训练Day10(思维 + DP, 模型:性质构造法) Problem - 545C - 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 
#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;
}

你可能感兴趣的:(算法)