1. 程式人生 > >從一個錯了一整天的"一眼題"說起 (二分,洛谷P1182 數列分段 Section II)

從一個錯了一整天的"一眼題"說起 (二分,洛谷P1182 數列分段 Section II)

部落格要寫的整潔。氣質是修煉不來的啊,體現在方方面面。

當代碼越來越長的時候,當你又需要這樣特判那樣特判的時候……

你幾乎已經錯了。_(:з」∠)_

再改改不出來只能說之前想出的演算法不適合吧……部落格寫工整一點

 

 

 

【二分】

最後想拿到的肯定是m=cnt&&rec=mid的,但是這個並不需要特判返回。

r<l的時候迴圈幾乎已經退出了呀,我們需要的ans

 

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

啊~ 如果一個題想要二分要滿足二分性的。

就是說,一長串,一半可以,另一半不行,我們二分的是那個分界線

可以的是滿足題意,但是多了,不可以的就是真的不可以。

我在上個題目“數的座標、扔石子”中的,前面不行後面不行中間去分吧,的解法,其實充其量只是一個列舉和縮小範圍的界限而已。要理解二分的本質,即是就是是,不是就是不是,不要這樣模稜兩可。

本題要二分的不是那個最大值,而是在滿足多少的前提下可以分成恰好m塊。

至於細節,如果分成了2塊(題目要求是3塊)那麼一定可以拆成3塊,只是沒拆的問題。即2是滿足條件的那一方,2的話就是分塊數量太少,因為最大值太大。

由此。題目中,右邊(上面)是可行的,下面(左邊)是不可行的,相應的二分程式也要改。

在抽同學二分模板中,cmp判斷為真(條件可行)時才更新。一併更改便好。

恭喜我在抽同學的瘋……狂提示下終於會了一點點二分。

他說這個很簡單的,程式碼肯定不超過20行。我。。

最後寫成這樣還行吧-、- (過程要理解,上面邊界是啥,下面邊界是啥。(⊙_⊙))

#include<algorithm>
#include<vector>
#include<iostream>
#include<queue>
#include<stdio.h>
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的範圍啊,,,