區間DP訓練

區間DP訓練

一、石子合併

  • 問題描述

    • 將 n (\(1 \le n \le 200\))堆石子繞圓形操場擺放,現要將石子有次序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆的石子數,記爲該次合併的得分。請編一程序,由文件讀入讀入堆數 n 及每堆的石子數。① 選擇一種合併石子的方案,使得做 n -1 次合併,得分的總和最小 。② 選擇一種合併石子的方案,使得做 n -1 次合併,得分的總和最大。
  • 輸入格式

    • 輸入第一行爲一個整數 n ,表示有 n 堆石子,第二行爲 n 個整數,分別表示每堆石子的數量。
  • 輸出格式

    • 輸出共 2 行,第一行爲合併得分總和最小值,第二行爲合併得分總和最大值。
  • 樣例輸入

    4
    4 5 9 4
  • 樣例輸出

    43 
    54
  • 代碼

    #include <cstdio>
    #include <iostream>
    
    using namespace std;
    
    const int maxn = 205,MAX = 0x7fffffff/2;
    int f1[maxn][maxn],f2[maxn][maxn],s[maxn][maxn] = {0};
    int a[maxn],sum[maxn] = {0},n,i,ans1,ans2;
    
    void init();
    void dp();
    
    int main()
    {
        init();
        dp();
        printf("%d\n%d\n",ans1,ans2);
        return 0;
    }
    
    void init()
    {
        scanf("%d",&n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            a[i+n] = a[i];
        }
        for(i = 1; i <= n*2; i++)
        {
            sum[i] = sum[i-1] + a[i];
            f2[i][i] = 0;
            f1[i][i] = 0;
        }
    }
    
    void dp()
    {
        int j,k,L;
        for(L = 2; L <= n; L++)
            for(i = 1; i <= 2*n-L+1; i++)
            {
                j = i+L-1;
                f1[i][j] = 0xfffffff/2;
                f2[i][j] = 0;
                for(k = i;k < j;k++)
                {
                    f1[i][j] = min(f1[i][j],f1[i][k] + f1[k+1][j]);
                    f2[i][j] = max(f2[i][j],f2[i][k] + f2[k+1][j]);
                }
                f1[i][j] += sum[j] - sum[i-1];
                f2[i][j] += sum[j] - sum[i-1];
            }
            ans1 = 0x7fffffff/2,ans2 = 0;
            for(i = 1;i <= n;i++)
                ans1 = min(ans1,f1[i][i+n-1]);
            for(i = 1;i <= n;i++)
                ans2 = max(ans2,f2[i][i+n-1]);
    }
    

二、能量項鍊

  • 問題描述

    • 在 Mars 星球上,每個 Mars 人都隨身佩戴着一串能量項鍊。在項鍊上有 N 顆能量珠。能量珠是一顆有頭標記和尾標記的珠子,這些標記對應着某個正整數。並且,對於相鄰的兩顆珠子,前一刻珠子的尾標記一定等於後一顆珠子的頭標記。因爲只有這樣,通過吸盤(吸盤是 Mars 人吸收能量的一種器官)的作用,這兩顆珠子才能聚合成一顆珠子,同時釋放出可以被吸盤吸收的能量。如果前一顆能量珠的頭標記爲 m,尾標記爲 r,後一顆能量珠的頭標記爲 r,尾標記爲 n,則聚合後釋放的能量爲 \(m * r * n\)(Mars 單位),新產生的珠子頭標記爲 m,尾標記爲 n。需要時,Mars 人就用吸盤夾住相鄰的兩顆珠子,通過聚合得到能量,直到項鍊上只剩下一顆珠子爲止。顯然,不同的聚合順序得到的總能量是不同的,請你設計一個聚合順序,使一串項鍊聚合後釋放的總能量最大。
    • 例如,設 N = 4,4 顆珠子的頭標記與尾標記依次爲(2,3),(3,5),(5,10),(10,2)。我們用記號 \(\oplus\) 表示兩顆珠子的聚合操作,\((j \oplus k)\) 表示第 j,k 兩顆珠子聚合後所釋放的能量。則第4,1 兩顆珠子聚合後釋放的能量爲:\((4\oplus1) = 10×2×3 = 60\)。這一串項鍊可以得到最優價值的一個聚合順序所釋放的總能量爲:\(((4\oplus1)\oplus2)\oplus3 = 10×2×3+10×3×5+10×5×10 = 710\)
  • 輸入文件

    • 輸入文件的第一行是一個正整數 N(\(4\le N \le 100\)),表示項鍊上珠子的個數。第二行是 N 個用空格隔開的正整數,所有的的數均不超過 1000 。第 i 個數爲第 i 顆珠子的頭標記(\(1 \le i \le N\)),當 \(i<N\) 時,第 i 顆珠子的尾標記應該等於第 i + 1 顆珠子的頭標記。第 N 顆珠子的尾標記應該等於第 1 顆珠子的頭標記。
    • 至於珠子的順序,你可以這樣確定:將項鍊放在桌面上,不要出現交叉,隨意指定第一顆珠子,然後按順時針方向確定其他珠子的順序
  • 輸出格式

    • 輸出文件只有一行,是一個正整數 E(\(R \le 2.1×10^9\)),爲一個最優聚合順序所釋放的總能量
  • 樣例輸入

    4
    2 3 5 10
  • 樣例輸出

    710
  • 代碼

    #include <cstdio>
    #include <iostream>
    
    using namespace std;
    
    int head[205],tail[205],f[205][205] = {0};
    
    int main()
    {
        int ans = 0,n,i,t,j,k;
        scanf("%d",&n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d",&head[i]);
            head[i+n] = head[i];
        }
        for(i = 1; i <= 2*n-1; i++)
            tail[i] = head[i+1];       //環變成鏈
        tail[2*n] = head[1];           //求尾標記
        for(i = 1; i <= 2*n-1; i++)   //初始化
            f[i][i] = 0;
        for(t = 1; t <= n-1; t++)   //階段,合併次數
            for(i = 1; i <= 2*n-t; i++)    //狀態,起始位置
            {
                j = i+t;                    //計算結束位置
                for(k = i; k <= j-1; k++)     //決策
                    f[i][j] = max(f[i][j],f[i][k] + f[k+1][j] + head[i]*tail[k]*tail[j]);
            }
        for(i = 1; i <= n; i++)
            ans = max(ans,f[i][i+n-1]);                        //求出最值
        printf("%d",ans);
        return 0;
    }

三、凸多邊形的劃分

  • 問題描述

    • 給定一個具有 N (\(N\le 50\)) 個頂點(從 1 到 N 編號)的凸多邊形,每個頂點的權均是一個正整數。問:如何把這個凸多邊形劃分成 N - 2 個互不相交的三角形,使得這些三角形頂點的權的乘積之和最小?
  • 輸入格式

    • 輸入文件的第一行爲頂點數 N,第二行爲 N 個頂點(從 1 到 N )的權值
  • 輸出格式

    • 只有一行,爲這些三角形頂點的權的乘積之和的最小值
  • 輸入樣例

    5
    122 123 245 231 121
  • 輸出樣例

    12214884
  • 代碼

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    typedef long long int ll;
    ll F[110][110][110],a[110];
    ll s1[110],s2[110],s3[110];
    int n;
    
    void Mark(ll c[])
    {
        for(int i = 1; i <= c[0]; i++)
        {
            c[i+1] += c[i]/10000;
            c[i] %= 10000;
        }
        while(c[c[0]+1])
        {
            c[0]++;
            c[c[0]+1] += c[c[0]]/10000;
            c[c[0]] %= 10000;
        }
    }
    
    void Mul(ll a1,ll a2,ll a3,ll c[])
    {
        c[0] = c[1] = 1;
        for(int i = 1; i <= c[0]; i++)
            c[i] *= a1;
        Mark(c);
        for(int i = 1; i <= c[0]; i++)
            c[i] *= a2;
        Mark(c);
        for(int i = 1; i <= c[0]; i++)
            c[i] *= a3;
        Mark(c);
    }
    
    void Add(ll a[],ll b[],ll c[])
    {
        if(a[0] > b[0])
            c[0] = a[0];
        else
            c[0] = b[0];
        for(int i = 1; i <= c[0]; i++)
            c[i] = a[i] + b[i];
        Mark(c);
    }
    
    int Compare(ll a[],ll b[])
    {
        if(a[0] < b[0])
            return 0;
        if(a[0] > b[0])
            return 1;
        for(int i = a[0]; i >= 1; i--)
            if(a[i] < b[i])
                return 0;
            else if(a[i] > b[i])
                return 1;
        return 0;
    }
    
    void Print()
    {
        int i;
        printf("%lld",F[1][n][F[1][n][0]]);
        for(i = F[1][n][0] - 1; i >= 1; i--)
        {
            printf("%lld",F[1][n][i]/1000);
            printf("%lld",F[1][n][i]/100%10);
            printf("%lld",F[1][n][i]/10%10);
            printf("%lld",F[1][n][i]%10);
        }
        printf("\n");
    }
    
    int main()
    {
        int i,j,k,t;
        scanf("%d",&n);
        for(i = 1; i <= n; i++)
            cin>>a[i];
        for(i = 1; i <= n; i++)
            for(j = 1; j <= n; j++)
                F[i][j][0] = 0;
        for(t = 2; t <= n-1; t++)
            for(i = 1; i <= n-t; i++)
            {
                j = i+t;
                F[i][j][0] = 60;
                for(k = i+1; k <= j-1; k++)
                {
                    memset(s1,0,sizeof(s1));
                    memset(s2,0,sizeof(s2));
                    memset(s3,0,sizeof(s3));
                    Mul(a[i],a[k],a[j],s1);
                    Add(F[i][k],F[k][j],s2);
                    Add(s1,s2,s3);
                    if(Compare(F[i][j],s3))
                        memcpy(F[i][j],s3,sizeof(s3));
                }
            }
        Print();
        return 0;
    }
posted @ 2018-08-24 19:24 Nikki_o3o 閱讀(...) 評論(...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章