1. 程式人生 > 實用技巧 >區間dp [D - Flood Fill] 暑訓第四天

區間dp [D - Flood Fill] 暑訓第四天

區間dp D - Flood Fill

題目大意:

如果[l,r] 這個連續的區間的數都相等,則說明這個是一個連通塊,給你n個數,每一個數都代表這個位置的顏色,首先你要選取第一個操作的位置,之後你可以對這個位置所在的連通塊進行改變顏色,可以改變成任意顏色。問最少多少次操作可以讓這n個數變成一個顏色。

題解:

首先要重視這個操作的位置只有一個,就是最開始選取的。

使得 \([l,r]\) 這個區間最後都是一樣的顏色,那麼只能從 \([l+1,r]\) \([l,l]\) 和 $[l,r-1] $ $ [r,r]$這兩個狀態轉移過來。

我們可以假設第一個選取的位置在 \([l+1,r]\)

或者 \([l,r-1]\),那麼 \([l,l]\) 或者 \([r,r]\) 則是要變成的顏色,那麼最後\([l,r]\) 這個區間的顏色就是 \([l,l]\) 或者 \([r,r]\)

如果是從 \([l+1,r]\) 這個狀態轉移過來,那麼最後這個顏色應該是和 \(l\) 這個位置的顏色是一樣的。

如果是從 \([l,r-1]\) 這個狀態轉移過來,那麼最後這個顏色應該是和 \(r\) 這個位置的顏色是一樣的。

所以需要第三維,對於區間 \([l,r]\) 0表示從 \(l\) 轉移過來,1表示從 \(r\) 轉移過來。

#include <bits/stdc++.h>
#define id first
#define val second
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn=5e3+10;
int dp[maxn][maxn][2],a[maxn];

int main(){
    int n;
    scanf("%d",&n);
    memset(dp,inf,sizeof(dp));
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),dp[i][i][0]=dp[i][i][1]=0;
    for(int len = 2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            dp[i][j][0]=min(dp[i+1][j][1]+(a[j]!=a[i]),dp[i+1][j][0]+(a[i+1]!=a[i]));
            dp[i][j][1]=min(dp[i][j-1][0]+(a[j]!=a[i]),dp[i][j-1][1]+(a[j-1]!=a[j]));
        }
    }
    printf("%d\n",min(dp[1][n][0],dp[1][n][1]));
    return 0;
}