2020年廣東工業大學第十屆文遠知行杯新生程式設計競賽 A.肥豬的鋼琴床(dp動態規劃)

Sankkl1發表於2020-12-07

題目連結:https://ac.nowcoder.com/acm/contest/9692/A

題目描述
肥豬很喜歡睡懶覺,與此同時肥豬十分喜歡買各種各樣神奇的床。

今天肥豬買了一張很寬很寬的鋼琴床,肥豬就在上面滾來滾去。
這張鋼琴床十分的有趣,因為它被分為n個部分,每一個部分是凸起來或者凹下去的,可是肥豬十分不喜歡相鄰兩個位置高低不同,因為他會滾不過去。
肥豬從小就很喜歡這樣的一句諺語:豬往高處走,水往低處流,所以肥豬很喜歡凸起來的部分而不喜歡凹下去的部分。
現在肥豬可以把床中的某一些部分拿走,以讓剩餘部分所有凸起來的部分都是連起來的,這樣肥豬就會很開心,因為他又可以滾來滾去了。
肥豬希望移除的部分最少

我們假設0表示某一個位置是凹下去的,1表示某一個位置是凸起來的。

那麼
0
1
00111
01111000
11100
都是肥豬喜歡的鋼琴床,因為所有凸起來的部分都是連續的

如果是
101
010011
1111101
就是肥豬不喜歡的鋼琴床,因為所有凸起來的部分不是連續的
對於第一張床,肥豬可以把第二個位置拿走,這樣床就變成了11。
對於第二張床,肥豬可以把第二個位置拿走,這樣床就變成了00011。

輸入描述:
讀入一共有兩行,第一行只有一個正整數數n,表示鋼琴床有n個部分。
第二行由一個01串組成

  • n<=1000000
  • 保證01串的長度為n
    輸出描述:
    輸出一個正整數表示肥豬最少要拿走的部分的數量

示例1

輸入

10
0001111000

輸出

0

分析

最終要變成的01串的形式肯定是類似 00011100 這樣的,那麼我們可以分三段(第一段0 第二段1 第三段0)來dp:
我們定義 dp[i][0], dp[i][1], dp[i][2] 分別表示到位置 i 第一段 0 刪掉了幾個 1,第二段 1 刪掉了幾個 0 ,第三段 0 刪掉了幾個 1 。

我們在 dp 過程中,如果 i 位置是 0 的話:

  1. 如果它是在第一段 0 中,0 不用刪去,則:dp[i][0] = dp[i - 1][0]
  2. 如果它是在第二段 1 中,則我們就要刪去它,有兩種情況,它可以是第二段的第一個 1 也可以不是,我們就要取小的那個值:dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + 1
  3. 同理如果它是第三段 0 中的,不用刪去,則推出:dp[i][2] = min(dp[i - 1][1], dp[i - 1][2]) + 1

i 位置是 1 的情況也同理。

程式碼

#include <bits/stdc++.h>
using namespace std;

const int N=2e6+10;
int n;
char a[N];
int dp[N][3];

void solve(){
    cin>>n;
    scanf("%s",a+1);
    for(int i=1;i<=n;i++){
        dp[i][0] = dp[i-1][0] + (a[i]-'0');
        dp[i][1] = min(dp[i-1][0] + '1' - a[i], dp[i-1][1] + '1' - a[i]);
        dp[i][2] = min(dp[i-1][1] + a[i] - '0', dp[i-1][2] + a[i] - '0');
        //cout<<i<<"-> "<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<endl;
    }
    cout<<min(dp[n][0], min(dp[n][1], dp[n][2]));
}
int main()
{
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

相關文章