藍橋杯 —— 石子合併問題 —— Dp
阿新 • • 發佈:2019-01-26
題目大意是說給你一個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; }