POJ-1651 Multiplication Puzzle 区间DP
题目链接:https://vjudge.net/problem/POJ-1651
题意:
题目的意思是给定一个数列,每次取出其中一个数字,价值增加这个数字与其左右两个数字的乘积,直到最后只剩两个数字,求价值的最小值。
分析:
首先这种一步步求最优解的问题,可以想到会不会是贪心问题?假设贪心可以解决,那么可以想到的策略就是每一步取使得增加价值最小的数字。但是事实是这种策略不是总正确的。反例如下:
数列为{ 10 1 50 50 20 5 },则按照这种策略得到的价值为
10*1*50 + 50*20*5 + 50*50*5 + 10*50*5 = 20500
但是事实上,最优策略得到的最小价值为3650。故此题不是贪心问题,应该考虑区间DP。
为什么会想到区间DP呢,因为这是分步求解最优问题,每一步的求解对结果都会产生影响,而且根据上面贪心的验证,我们知道这不是一个贪心问题,所以应该就是区间DP问题无疑了。
应用区间DP的思路就是,求出每一段区间的价值最小值,逐渐增大范围扩散至整个区间。
代码如下:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #define inf 0x3fffffff const int maxn = 550; using namespace std; int arr[maxn]; int dp[maxn][maxn]; int n; void DP() { for(int cnt = 3; cnt <= n; cnt++) { for(int i = 0; i < n-2; i++) { int j = i+cnt-1; dp[i][j] = inf; for(int k = i+1; k < j; k++) { dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+arr[i]*arr[k]*arr[j]); } } } } int main(void) { while(scanf("%d", &n) == 1) { for(int i = 0; i < n; i++) { scanf("%d", &arr[i]); } DP(); printf("%d\n", dp[0][n-1]); } }