POJ 1651 Multiplication Puzzle (区间DP OR 记忆化搜索)

(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
",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
", DP(0, nCard-1)); //右参数不是nCard
    }
    return 0;
}




原文地址:https://www.cnblogs.com/zswbky/p/6792898.html