(1)分析最优子结构 在这个问题中想得到a b c d e f之间最小值,比如开始拿d位置的,那么问题就转化成了a b c e和c e f这两个子问题的最小值再加上c*d*e就行了,不过如果真的是这么想的话,也就是把d当成最开始拿的话,怎么表示?开始还想用dp[i][j][k]表示区间i~j之间去掉k后的最小值,但是再解决这个子问题的时候有需要加一维,这样表示就很困难了,虽然子问题分离出来了,但是表示很困难。所以我们可以在重新从另外一种思路想,就是把d当做最后拿的,也就是说在拿d之前b
c e已经拿完了,这样就变成了a b c d 和d e f 两个子问题+a*d*f了,表示起来也就简单了,直接用二维表示区间里面的就好了。
(2)确定dp状态的含义 dp[i][j]区间i~j能得到的最小值
(3)得到递推方程式
dp[j][j + i] = min(dp[j][j + i] , dp[j][k] + dp[k][j + i] + a[k] * a[j] * a[j + i]);
这里突然总结出一个技巧来,就是区间dp因为是需要求区间值的,所以复杂度一般就是O(n^3)(纯二维数组),但是一般DP都是O(n^2)(一般都能用一维数或者滚动数组代替二维数组),并且一般ACM题型判断属于DP类型的是很简单的,但是具体那种DP可能在划分子问题时候还是不太好想的,现在我们就可以用题目中给的数据范围来推测是那种DP了,最起码心里有个数了,算是一种辅助的方式吧!
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string.h> #define INF 0x3f3f3f3f using namespace std; int dp[105][105]; int main() { int n,i,num[105],len,j; while(~scanf("%d",&n)) { memset(num,0,sizeof(num)); for(i=1;i<=n;i++) scanf("%d",&num[i]); for(i=1;i<n-1;i++) dp[i][i+2]=num[i]*num[i+1]*num[i+2]; for(len=4;len<=n;len++) ///先更新小区间,最后得到大区间 { for(i=1;i<=n-len+1;i++) ///开始位置 { dp[i][i+len-1]=INF; for(j=i+1;j<i+len-1;j++) ///最后拿第j个 dp[i][i+len-1]=min(dp[i][i+len-1],dp[i][j]+dp[j][i+len-1]+num[i]*num[j]*num[i+len-1]); } } printf("%d\n",dp[1][n]); } return 0; }
Top-Down方式:
#include<stdio.h> #include<string.h> #include <iostream> //用 INT_MAX 要此头文件 int point[100], dp[100][100]; int DP(int left, int right) { int i, min, l, r, temp; if(-1 != dp[left][right]) return dp[left][right]; if(right-left == 1) return dp[left][right] = 0; min = INT_MAX; for(i=left+1; i<right; i++) { l = DP(left, i); r = DP(i, right); temp = l + r + point[left] * point[i] * point[right]; if(min > temp) min = temp; } return dp[left][right] = min; } int main() { int nCard, i; while(scanf("%d", &nCard) != EOF) { memset(dp,-1,sizeof(dp)); //赋值-1 for(i=0; i<nCard; i++) scanf("%d", &point[i]); //忘记 & ; printf("%d\n", DP(0, nCard-1)); //右参数不是nCard } return 0; }