如果没有人告诉你矩阵连乘问题就应该用动态规划的方法来解决,那么我们应该如何想到呢?
wiki:
动态规划是这样子的这里有对矩阵连乘问题的描述。首先应该对问题进行抽象,如果能够了解问题中矩阵的部分,那么问题可以抽象成这样
poj1651。这里问题的另一种简单的表示方式就是:给定一列数,每次你可以从中抽取1个数(除去头尾两个数不可以抽取),设置一个score,当你抽取该数的时候,score要加上该数和左右两个数的乘积,问抽取到最后只剩下头尾两个数的时候,怎样的抽取顺序可以使score的值最小呢?
很直观的方法就是枚举每种抽取方式,然后找出使score最小的那一次抽取。(这被称为笨办法)
先设有n个要抽取的数,也就是总数为n+2。我们试着从中抽取m个,那么我们会发现在省下的那些还没被抽取的数字中应该存在一种抽取策略使得它们的score最小(
最优子结构,这里可以用简单的反证法说明),换句话说,就是我们前面怎样的抽取顺序对后面不会造成影响。这里就说明了笨办法为什么笨了:如果我们找出了后面抽取的最优策略后,那么每次我们改变前面的m个数的抽取顺序时,是不需要对后面抽取顺序进行枚举的,只有用最优那个策略即可(
重叠子问题)。
那么这样说的话,只要找出前面抽取的最优策略和后面抽取的最优策略的话,那么就可以找出这样的结果:以先抽取m个为分界限的最优解。那么要求抽取n个球的问题时,就需要从1开始到n/2为分界限的最优解。然后再对每个子问题进行递归的求解,当n=1时那么问题无需再进行分解。
上面这样子理解有个缺点:很难用计算机语言实现。问题在于先抽取m个数,这些数的位置不连续。其实把它改为连续的对题目的求解也是一样的,不过这时候要找的就不是从1到n/2为分界限的最优解了(这样的话就不全面)。应该从开头的1,一直到n-1进行找最优解。
这是poj1651的代码:
1 #include<iostream>
2 using namespace std;
3 const int inf = 0xffffff;
4 int dp[101][101];
5 int num[101];
6 void input(int n){
7 for(int i = 1 ; i <= n; i++)
8 cin>>num[i];
9 for(int i = 0; i <= n; i++)
10 for(int j = 0 ; j <= n; j++)
11 dp[i][j] = inf;
12 }
13 int solve(int a,int b){
14 if(dp[a][b] != inf)return dp[a][b];
15 if(b - a == 2){
16 dp[a][b] = num[a]*num[a+1]*num[b];
17 return dp[a][b];
18 }
19 if(b - a == 1){
20 dp[a][b] = 0;
21 return dp[a][b];
22 }
23 int min = inf;
24 int temp;
25 for(int i = a+1 ; i < b; i ++){
26 temp = solve(a,i) + solve(i,b) + num[a]*num[i]*num[b];
27 if(temp < min) min = temp;
28 }
29 dp[a][b] = min;
30 return dp[a][b];
31 }
32 int main(){
33 int n;
34 while(cin >> n){
35 input(n);
36 cout << solve(1,n)<<endl;
37 }
38 }