从一个错了一整天的"一眼题"说起 (二分,洛谷P1182 数列分段 Section II)

博客要写的整洁。气质是修炼不来的啊,体现在方方面面。

当代码越来越长的时候,当你又需要这样特判那样特判的时候……

你几乎已经错了。_(:з」∠)_

再改改不出来只能说之前想出的算法不适合吧……博客写工整一点

 

 

 

【二分】

最后想拿到的肯定是m=cnt&&rec=mid的,但是这个并不需要特判返回。

r

 

emmm...错了一天 每次都有新的错法

啊~ 如果一个题想要二分要满足二分性的。

就是说,一长串,一半可以,另一半不行,我们二分的是那个分界线

可以的是满足题意,但是多了,不可以的就是真的不可以。

我在上个题目“数的坐标、扔石子”中的,前面不行后面不行中间去分吧,的解法,其实充其量只是一个枚举和缩小范围的界限而已。要理解二分的本质,即是就是是,不是就是不是,不要这样模棱两可。

本题要二分的不是那个最大值,而是在满足多少的前提下可以分成恰好m块。

至于细节,如果分成了2块(题目要求是3块)那么一定可以拆成3块,只是没拆的问题。即2是满足条件的那一方,2的话就是分块数量太少,因为最大值太大。

由此。题目中,右边(上面)是可行的,下面(左边)是不可行的,相应的二分程序也要改。

在抽同学二分模板中,cmp判断为真(条件可行)时才更新。一并更改便好。

恭喜我在抽同学的疯……狂提示下终于会了一点点二分。

他说这个很简单的,代码肯定不超过20行。我。。

最后写成这样还行吧-、- (过程要理解,上面边界是啥,下面边界是啥。(⊙_⊙))

#include
#include
#include
#include
#include
using namespace std;
int a[100005];
int n, m;
int cmp(int mid) {
    int temp = 0; int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (temp + a[i] <= mid)temp = temp + a[i];
        else if (temp + a[i]>mid){
            cnt++;
            temp = a[i];
        }
    }if (temp != 0)cnt++;
    if (cnt>m)return 0;
    return 1;
}
int main() {
    cin >> n >> m;	
    int kkk = 0;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
        kkk = max(kkk, a[i]);
    }
    int l = kkk; int r = sum;int ans = 0;
    while (l <= r) {
        int mid = (l + r) / 2;
        int xxx = cmp(mid);
        if (xxx == 1){
            r = mid - 1;
            ans = mid;
        }
        else l = mid + 1;
    }
    cout << ans << endl;
    return 0;
}

 

 

 

【动态规划】

前面做的。

(1)dp[0][0]=1

(2)在任何情况停下来都是最优解法

(3)此种情况可由正好两种情况推理而来/ 有两种可以促使它拿到今天的结果

(4)二维状态下坐标表示的是什么

如本题中dp[i][j]即第i秒在位置j处。想出了这一点继续推理也不复杂了

***记忆化搜索。

若更新过记录肯定不等于0,直接返回即可。【否则每次访问到都需要重新递归!!!!】

简单的一小句话救了TLE的命。

30*30的范围啊,,,

你可能感兴趣的:(从一个错了一整天的"一眼题"说起 (二分,洛谷P1182 数列分段 Section II))