区间DP入门
石子归并
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
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
If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
Input
Output
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 }
好啦,今天任务完成,去吃饭饭啦~~~耶耶耶!!!