Multiplication Puzzle(区间DP:类似矩阵连乘问题的DP模板)

来源:互联网 发布:hash算法特点 编辑:程序博客网 时间:2024/05/21 06:01


Link:http://poj.org/problem?id=1651




Multiplication Puzzle
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 7424 Accepted: 4596

Description

The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row. 

The goal is to take cards in such order as to minimize the total number of scored points. 

For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring 
10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000

If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be 
1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150.

Input

The first line of the input contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces.

Output

Output must contain a single integer - the minimal score.

Sample Input

610 1 50 50 20 5

Sample Output

3650

Source

Northeastern Europe 2001, Far-Eastern Subregion



题意:给出一组数,每删除一个数的代价是其左右两边的数与之连乘的数值,求除了首尾两个数之外,删除中间所有数的最小代价是多少。


编程思想:区间DP。详见下面代码注释。


AC code:

#include<iostream>#include<algorithm>#include<stdio.h>#include<cstring>#include<cmath>#include<vector>#include<string.h>using namespace std;const int INF=0x3f3f3f3f;int dp[111][111];int card[111];/*dp[i][j]表示删除区间i+1至j-1的最小代价,其中j>=i+2,所以转态转移方程为:dp[i][j]=min(card[i]*card[k]*card[j]+dp[k][j]+dp[i][k]),k=i+2~j-2;k是最后一个删除的位置注意初始化问题:这里k不从i+1开始和j-1结束的原因是这两个点必须作为初始化用,不然递推式中dp[k][j]和dp[i][k]的值一开始没初始化,不知道其值是多少,无法进行递推而这里初始化方式类似类似递归时二分的那种分治思想,初始化dp[i][k]相当于求左边,初始化dp[k][j]相当于求右边,最后求中间的k时刚好利用前面得到的左右边的值,从而退出中间的结果,其实说白了,这题也可以用递归的方式做,只不过我把递归转化为递推罢了。i到j的牌简单表示如下:i ,i+1 ,k(i+2,i+3……j-2),j-1,j当最后取出的牌是k(k可能是i+2,i+3……j-2中的一个)时,当然也可能最后取出的牌是i+1或j-1,反正取全部中的最小值就是了,dp[i][j]=min(card[i]*card[k]*card[j]+dp[k][j]+dp[i][k]),k=i+2~j-2;而此时的dp[i][k]和dp[k][j]必须先求出来,其实一开始没想这么多,看dp[i][j]中的j必须比i至少大2时,就应该想到枚举k时只能从i+2开始,j-2结束!!!而i+1和j-1必须先求!!!结论:递推式是由左右两边推出来的式子,一般要先在左右两边的边界进行初始化!!!*/int main(){   //freopen("D:\\in.txt","r",stdin);   int i,j,k,n;   while(scanf("%d",&n)!=EOF)   {       for(i=1;i<=n;i++)       {           scanf("%d",&card[i]);       }       memset(dp,0,sizeof(dp));       for(i=n-2;i>=1;i--)       {           for(j=i+2;j<=n;j++)           {               dp[i][j]=card[i+1]*card[i]*card[j]+dp[i+1][j];//删除左边边界i+1               dp[i][j]=min(dp[i][j],card[j-1]*card[i]*card[j]+dp[i][j-1]);//删除右边边界j-1               for(k=i+2;k<=j-2;k++)               {                   dp[i][j]=min(dp[i][k]+card[k]*card[i]*card[j]+dp[k][j],dp[i][j]);               }           }       }       printf("%d\n",dp[1][n]);   }    return 0;}


0 0
原创粉丝点击