1. 程式人生 > >藍橋杯 —— 石子合併問題 —— Dp

藍橋杯 —— 石子合併問題 —— Dp

題目大意是說給你一個n,代表有n堆石子。然後給你n個數,分別表示每堆石子的個數。

要求是每次只能合併相鄰的兩堆石子,每合併一次就把ans += 需要被合併的兩堆石子的個數。

問怎樣合併使得最後所需要的費用 ans 值最小。

解題思路:這道題不能夠用區域性最優的貪心思想來做,而是求得全域性最優解。分段取區間,作為一個全域性的區間段,如果這個子區間段達到了全域性最優解,那麼最終的長度為n的結果也同樣是最優的解。

dp[i][j] 表示從i -> j 的最小花費。

舉個栗子:dp[i][j] = min(dp[i][k] + dp[k+1][j] + sum);

k表示i -> j中間的結點,sum則表示i->j堆石子的總個數。因為是把dp[i][j] 拆開遍歷的,所以說最後應該加一個sum。

當然第一眼沒仔細讀題,沒有看到是隻能合併相鄰的兩堆石子,所以我直接用了優先佇列做了一遍。哈哈...

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <map>
#include <sstream>
#include <queue>
#include <stack>
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a));
#define For(a,b) for(int i = a;i<b;i++)
#define ll long long
#define MAX_N 100010
using namespace std;

int ans[MAX_N];
int dp[1005][1005];

void DP(int n)
{
    for(int i = 0; i<=n; i++){
        dp[i][i] = 0;
    }
    for(int j = 2; j<=n; j++)
    {
        for(int i = 1; i<=n-j+1; i++)
        {
            int num = i + j - 1;
            dp[i][num] = dp[i+1][num] + ans[num] - ans[i-1];
            for(int k = i+1; k<num; k++)
            {
                int mid = dp[i][k] + dp[k+1][num] + ans[num] - ans[i-1];
                dp[i][num] = min(dp[i][num],mid);
            }

        }
    }
    return ;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        mem(ans,0);
        for(int i = 1; i<=n; i++)
        {
            int stone;
            scanf("%d",&stone);
            ans[i] += ans[i-1] + stone;
        }
        DP(n);
        printf("%d\n",dp[1][n]);
    }
    return 0;
}