POJ 1651 Multiplication Puzzle DP 类似矩阵链

对整数序列(a0~an-1)求和,求和规则:不断取ai(0<i<n),和加上ai-1*ai*ai+1,去掉ai,重复以上步骤直到只剩两个数。求最小和。

思路:

类似矩阵链相乘,设dp[i][j]为序列(i,j)之间最小的sum。对序列(i,j),ai和aj最后会留下,所以最后一次求和一定是ai*ak*aj,(i<k<j)。以k为划分,则状态转移方程为dp[i,j] = min ( dp[i,j] , dp[i,k] + dp[k,j] + ai*ak*aj)

依赖关系分析:

求dp[i,j]要先求[i,k][k,j],即大区间依赖于小区间,因此从区间长度最小的开始遍历。要遍历全部小区间,遍历起点。所以有

for len = min_len to n
      for start = 0 to n-1
          /*状态转移,遍历k */

 

初始状态:

dp设为无穷大,区间长度最小为3,可以先将dp[i,i+2]算好,递推时len从4开始枚举。这里dp[i,i+1]要设为0,做题时WA卡在这里,因为更新状态时k从i+1开始枚举,而j=i+len-1,则会出现dp[i,j] = dp[i,i+2]=dp[i,i+1] + dp[k,j] + ai*ak*aj,为了保证计算正确应该赋值为0。

算法步骤:

初始化,然后递推求dp[i][j][k]。

算法复杂度

时间复杂度O(n3),空间复杂度O(n3)

代码:

1、递推

#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;;
const int inf = 0x3f;
int n,a[110];
int dp[110][110];
int main() 
{  
   cin >> n;
   for(int i=0;i<n;++i)
       scanf("%d",&a[i]);
    // dp 初始化
   memset(dp,inf,sizeof(dp));
   for (int i=0;i<n-1;++i)
       dp[i][i + 1] = 0;
   for (int i=0;i<n-2;++i)
       dp[i][i+2] = a[i]*a[i+1]*a[i+2];
   for(int len=4;len<=n;++len){
       for(int i=0;i<n;++i){
          int j=i+len-1;
          if(j>n) break;
          // find a minimal state for dp[i][j]
          for(int k=i+1;k<j;++k)
             dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
        }    
    } 
    cout << dp[0][n-1] << endl; 
    return 0;
}

 

2、记忆化搜索

主函数执行f(0,n-1)就是答案,记忆化搜索代码如下:

int f (int a, int b){
    if(dp[a][b]!=-1)  return dp[a][b]; // 已经计算过了
    if(b-a==1)  return dp[a][b] = 0;  // dp[i,i+1]
    int min = MAXN;
    for(int i = a+1;i <= b-1;i++){
        int l = f(a,i),  r = f(i,b);
        if (min > l + r + x[a]*x[i]*x[b])
            min = l + r + x[a]*x[i]*x[b];
    }   
    return dp[a][b] = min;
}
原文地址:https://www.cnblogs.com/tinyork/p/5044048.html