區間DP

區間DP,對于每段小區間,它的最優值是由更小的區間的最優值得出的,由此往下劃分,直到單個元素,由他們的組合合并得出最優解。

484感覺這個有套路

首先要分析題目,找最優子區間,分清取左區間邊界,還是右區間邊界

總結了一個區間的套路板子,這個過程也可以用遞歸實現

for (int l = st; l < len-(); l++) //l:區間長度
    for (int i = st; l < len-l+1; l++) // i:區間起點
    {
        int j = i+l-1; //j: 區間終點
        for (int k = i; k < j; k++) // 狀態轉移過程
            dp[i][j] = MAX/MIN(f[i][j], f[i][k]+f[k+1][j]+w[i][j]);
            //f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] } 
            // 這個484很像最短路/最小生成樹的松弛過程
    }

下面來看幾個栗子吧
POJ1651(石子合并類)
大致題意就是給你一個數列,從非頭尾的數字中取數字,求這些數字與左右數字乘積之和,求最小和。很明顯的石子合并問題,求每個區間最小值,合并得出區間最小值。

#define LL long long
#define INF 1e9
const int N = 101;
LL dp[N][N], p[N];
int vis[N];

int main()
{
    int n;
    scanf("%d", &n);
    int minn=-INF, cur;
    for (int i = 1; i <= n; i++)
        scanf("%lld", &p[i]);
       
    for (int m = 2; m <= n-1; m++) //可取區間[2, n-1]
        for (int i = 2; i <= n-m+1; i++) //區間起點,第二個2數字
        {
            int j = m+i-1; //區間終點,最小組合區間長度3
            dp[i][j] = INF;
            for (int k = i; k < j; k++)
                dp[i][j] = min(dp[i][j], dp[i][k] + p[i-1]*p[j]*p[k] + dp[k+1][j]);
        }
    printf("%I64d\n", dp[2][n]);
}

POJ2955(括號合并問題)

const int  N = 120;
int dp[N][N];
string s;

int main()
{
    while(cin >> s)
    {
        if(s == "end") break;
        CLR(dp);
        int len = s.size();
        for(int i = 1; i < len; i++)
        {
            int k = 0;
            for(int j = i; j < len; j++)
            {
                if(s[k]=='('&&s[j]==')' || s[k]=='['&&s[j]==']')
                    dp[k][j] = dp[k+1][j-1] + 2;

                for(int l = k; l < j; l++)
                    dp[k][j] = max(dp[k][j], dp[k][l]+dp[l+1][j]);
                k++;
            }
        }
        printf("%d\n", dp[0][len-1]);
    }
    return 0;
}

hdu4283
大致意思就是導演想要知道怎么使男孩的失望值最低,因為題目說的是先入后出,所以要反過來枚舉

#define CLR(x) memset(x, 0, sizeof(x))
const int N = 105;
int D[N], dp[N][N];
int sum[N];

int main()
{
    int T;
    scanf("%d", &T);
    int k = 1;
    while (T--)
    {
        CLR(D); CLR(dp); CLR(sum);
        sum[0] = 0;
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &D[i]);
            sum[i] = sum[i-1] + D[i]; // get所有區間的值
        }

        for (int i = n; i >= 1; i--)
            for (int j = i; j <= n; j++)
            {
                dp[i][j] = INF;
                for (int d = 1; d <= j-i+1; d++)
                {
                    int k = i+d-1;
                    dp[i][j] = min (dp[i][j], dp[i+1][k]+dp[k+1][j]+(d-1)*D[i]+d*(sum[j]-sum[k]));
                }
            }

        printf("Case #%d: %d\n", k++, dp[1][n]);
    }
}

hdu2476
這題題目有毒!!!!
阿西吧,讀了兩個小時沒讀懂,可能是我菜吧,最后查了題解才搞懂題目意思
分析一下樣例,他題目中所謂的刷一下是這樣的

0:zzzzzfzzzzz
1:aaaaaaaaaaa      [0,10]
2:abbbbbbbbba   [1,9]
3:abcccccccba      [2,8]
4:abcdddddba     [3,7]
5:abcdeeedba      [4,6]
6:abcdefedba       [5]

上代碼

const int N = 105;
int dp[N][N];
char a[105], b[105];
int ans[105];

int main()
{
    while (scanf("%s%s", a, b) != EOF)
    {
        int len = strlen(a);
        for (int i = 0; i < len; i++)
        {
            for (int j = i; j >= 0; j--)
            {
                dp[j][i] = dp[j+1][i] + 1;
                for (int k = j+1; k <= i; k++)
                    if (b[j] == b[k])
                        dp[j][i] = min(dp[j][i], dp[j+1][k]+dp[k+1][i]);
            }
            ans[i] = dp[0][i];
        }
        for (int i = 0; i < len; i++)
        {
            if (a[i] == b[i]) ans[i] = ans[i-1];
            else
            {
                for (int j = 0; j < i; j++)
                    ans[i] = min(ans[i], ans[j]+dp[j+1][i]);
            }
        }
        printf("%d\n", ans[len-1]);
    }
}

下面就是筆者之前提過,真正有可能在比賽中出現的區間DP,帶幾何計算的。
ZOJ3537
大致題意:給你一堆的點(二維坐標的),問又他們構成的多邊形能不能切成三角形,如果可以求出它的最小代價,對,每切一刀都是有代價的(costi, j = |xi + xj| * |yi + yj| % p. )
這是個凸包判斷+三角剖分+區間DP的問題
GG上板子上板子

#define CLR(x) memset(x, 0, sizeof(x))
int dp[305][305], cost[305][305];
int n, P;
struct point
{
    double x, y;
} p[305], tmp[305];

bool mult(point st, point ed, point p)
{
    return (st.x-p.x)*(ed.y-p.y) >= (ed.x-p.x)*(st.y-p.y);
}
bool operator < (const point &a, const point &b)
{
    return a.y<b.y || (a.y==r.y && a.x<b.x);
}

int Graham(point pnt[], int n, point res[])
{
    sort(pnt, pnt + n);
    if (n == 0) return 0; res[0] = pnt[0];
    if (n == 1) return 1; res[1] = pnt[1];
    if (n == 2) return 2; res[2] = pnt[2];

    int top = 1;
    for (int i = 2; i < n; i++)
    {
        while (top && mult(pnt[i], res[top], res[top-1])) top--;
        res[++top] = pnt[i];
    }

    int len = top;
    for (int i = n - 3; i >= 0; i--)
    {
        while (top!=len && mult(pnt[i], res[top], res[top-1])) top--;
        res[++top] = pnt[i];
    }
    return top;
}

int cal(int i, int j)
{
    return abs((int)tmp[i].x+(int)tmp[j].x) * abs((int)tmp[i].y+(int)tmp[j].y) % P;
}

int main()
{
    while (scanf("%d%d", &n, &P) != EOF)
    {
        CLR(p); CLR(tmp); CLR(cost);
        for (int i = 0; i < n; i++)
            scanf("%lf%lf", &p[i].x, &p[i].y);
        if (n <= 3) printf("0\n");
        else if (Graham(p, n, tmp) < n) printf("I can't cut.\n");
        else
        {
            for (int i = 0; i < n; i++)
                for (int j = i+2; j < n; j++)
                    cost[i][j] = cost[j][i] = cal(i, j);

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                    dp[i][j] = INF;
                dp[i][(i+1)%n] = 0; //dp[i][i+1]=0(i!=n-1) dp[n-1][0]=0
            }

            for (int i = n-3; i >= 0; i--)
                for (int j = i+2; j < n; j++)
                    for (int k = i+1; k <= j; k++)
                        dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);
            printf("%d\n", dp[0][n-1]);
        }
    }
    return 0;
}

這題的區間DP公式很簡單。。。就是幾何計算坑!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容