区间DP入门

石子归并

 51Nod - 1021 

N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
 
例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
 
括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
 

Input第1行:N(2 <= N <= 100) 
第2 - N + 1:N堆石子的数量(1 <= Aii <= 10000)Output输出最小合并代价Sample Input

4
1
2
3
4

Sample Output

19
题解:区间dp入门题,dp[i][j]表示从第i堆石子到第j堆石子合并的最小代价。区间dp一般都是从小区间开始求得最优解然后扩展到大的区间,而求大区间的最优解的时候用到的小区间的最优解已经求过,所以直接拿来用即可。
所以说区间动规一般都是三层for循环, 前两层用来控制区间长度, 最后一层用来枚举最后一次的位置, 还有需要注意的是区间用从小到大, 因为动态规划就是后面的用到前面的出的结果递推后面的结果。
1 //合并i到j的所有石子。那前一状态一定是两堆石子。
2 //这步我们就枚举所有可能的位置(两堆石子分开的位置)  
3 for(int k = i; k < j; k++)
4  {
5      if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[i][j])
6          dp[i][j] = dp[i][k] + dp[k+1][j] + sum[i][j];
7  }

4个数(1,2,3,4) 
某区间(i到j)相距为1时 d = 1 可求出f[1][2] = 3; f[2][3] = 5; f[3][4] = 7; 
d = 2时 , f[1][3] = min(f[1][2] + f[3][3], f[1][1] + f[2][3])+sum[1][3]= 9; (这里f[3][3] = 0,应为合并自己没花费)。同理f[2][4] = 14; 
d = 3时:f[1][4] = 19; 
枚举前一状态 f[1][4] = min(f[1][1]+f[2][4], f[1][2]+f[3][4], f[1][3] + f[4][4]) + sum[1][4];到这有点眉目没。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #define min(a,b) a<b?a:b
 5 #define max(a,b) a>b?a:b
 6 #define inf 0x3f3f3f3f
 7 using namespace std;
 8 const int maxn=110;
 9 int n;
10 int a[maxn];
11 int sum[maxn];
12 int dp[maxn][maxn];
13 int main()
14 {
15     cin>>n;
16     for(int i=1;i<=n;i++)
17     {
18         scanf("%d",&a[i]);
19         sum[i]=sum[i-1]+a[i];
20     }
21     memset(dp,0,sizeof(dp));
22     for(int len=2;len<=n;len++)
23     {
24         for(int i=1;i<=n-len+1;i++)
25         {
26             int j=i+len-1;
27             dp[i][j]=inf;
28             for(int k=i;k<j;k++)
29             {
30                 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
31             }
32         }
33     }
34     printf("%d\n",dp[1][n]);
35     return 0;
36 }

Multiplication Puzzle

 POJ - 1651

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

6
10 1 50 50 20 5

Sample Output

3650

题意:是n个数相乘,每次从中抽取一个数出来与相邻两个数相乘,直到抽到只剩两个数字,第一个数和最后一个数不能抽。

题解:和上一个题套路基本一致,大概框架为三个for循环,第一层for循环遍历区间长度,第二层for循环遍历区间的起始点i,以及终止点用j表示,第三层for循环遍历断点。就此题而言,len从3开始,表示区间长度最短为3, dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k])为核心,例如i=1 j=7 k=5表示[1,7]中,最后剩下的三个数为a[1],a[5],a[7],下一步的操作为拿掉a[5],总代价加上a[1]*a[5]*a[7],可以进行这步操作的前提是a[2]到a[5]中只剩下a[5],a[6]到a[7]中也只剩下a[7],及前面dp[1][5]早已求出,后面的dp[5][7]也早就求出,所以求dp[1][7]在以5为断点时的值就是dp[1][7]=min(dp[1][7],dp[1][5]+dp[5][7]+a[1]*a[5]*a[7])

上代码:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #define min(a,b) a<b?a:b
 5 #define inf 0x3f3f3f3f
 6 using namespace std;
 7 const int maxn=110;
 8 int dp[maxn][maxn];
 9 int a[maxn];
10 int n;
11 int main()
12 {
13     cin>>n;
14     for(int i=1;i<=n;i++)
15     {
16         scanf("%d",&a[i]);
17     }
18     memset(dp,0,sizeof(dp));
19     for(int len=3;len<=n;len++)
20     {
21         for(int i=1;i<=n-len+1;i++)
22         {
23             int j=i+len-1;
24             dp[i][j]=inf;
25             //cout<<i<<" "<<j<<endl;
26             for(int k=i;k<j;k++)
27             {
28                 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]);
29             }
30         }
31     }
32     printf("%d\n",dp[1][n]);
33 }

 

好啦,今天任务完成,去吃饭饭啦~~~耶耶耶!!!


 
posted @ 2018-10-08 17:45  *starry*  阅读(132)  评论(0编辑  收藏  举报