[BZOJ 1032][JSOI 2007]祖瑪 題解(區間DP)

[BZOJ 1032][JSOI 2007]祖瑪

Description

https://www.lydsy.com/JudgeOnline/problem.php?id=1032php

Solution

1.考慮初始化的方式。html

因爲同色轉移起來複雜,咱們考慮把相鄰的同色的球縮成一個球,記錄下縮後的球表明的原來的個數。ios

這時咱們考慮對刷的表的初始化,f[L][R]表示[L,R]區間中須要打入的最小珠子數。git

因爲是最小個數答案,因此所有初始化爲正無窮,但對於縮後的狀態,咱們考慮不受其餘合併時影響的結果:spa

  • 若是個數爲1,那麼此時須要打入兩個同色球,f[i][i]=2;code

  • 若是個數大於等於二,那麼此時只須要打入一個同色球,f[i][i]=1;htm

即:for(int i=1;i<=list[0];++i)f[i][i]=num[i]>1?1:2;blog

2.考慮DP的方式:常見的枚舉斷點組合求解。ip

可是咱們注意此題的特殊性質:合併過程當中兩端球個數大於等於3直接消掉,因此若是兩端顏色相等,咱們枚舉斷點前先判斷:get

  • 若是兩端球數合起來多於兩個,那麼直接等於左右向中間各縮進一個的答案;

  • 若是合起來等於兩個,那麼還須要再打入一個;

即: if(list[l]==list[r]) f[l][r]=f[l+1][r-1]+(num[l]+num[r]>2?0:1);

剩餘部分直接枚舉斷點鬆弛大的區間便可。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define R register
using namespace std;

int num[510],list[510],f[510][510];

inline int rd(){
    int x=0;
    bool f=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-') f=0;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f?x:-x;
}

int init(int n){
    int x,cnt=1;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            f[i][j]=0x3f3f3f3f;
    list[++list[0]]=rd();
    for(int i=2;i<=n;++i){
        x=rd();
        if(x==list[list[0]])++cnt;
        else{
            num[list[0]]=cnt;
            list[++list[0]]=x;
            cnt=1;
        }
    }
    num[list[0]]=cnt;
    for(int i=1;i<=list[0];++i)f[i][i]=num[i]>1?1:2;
}

int main(){
    init(rd());
    for(int len=2;len<=list[0];++len)
        for(int l=1;l<=list[0]-len+1;++l){
            int r=l+len-1;
            if(list[l]==list[r]) f[l][r]=f[l+1][r-1]+(num[l]+num[r]>2?0:1);
            for(int k=l;k<r;++k) f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
        }
    printf("%d",f[1][list[0]]);
    return 0;
}

PS:對於三個離散的點彙集在一塊兒的狀況,標程是錯誤的,因此當年數據聽說沒有那種狀況的,至今貌似尚未比較完美的解法,特判就行了......

有關區間DP的其餘講解參考個人隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/9038198.html

相關文章
相關標籤/搜索