【CSU-1592】解题报告(区间DP入门)

原始题目

1592: 石子归并

  • Time Limit: 1 Sec
  • Memory Limit: 128 Mb
  • Submitted: 896
  • Solved: 421

Description

现在有\(n\)堆石子,第\(i\)堆有\(a_i\)个石子。现在要把这些石子合并成一堆,每次只能合并相邻两个,每次合并的代价是两堆石子的总石子数。求合并所有石子的最小代价。

Input

第一行包含一个整数$T ( T 50) $ ,表示数据组数。

每组数据第一行包含一个整数$ n ( 2 n 100 ) $,表示石子的堆数。

第二行包含\(n\)个正整数$ a_i ( a_i 100)$,表示每堆石子的石子数。

Output

每组数据仅一行,表示最小合并代价。

Sample Input

2
4
1 2 3 4
5
3 5 2 1 4

Sample Output

19
33

Hint

Source

国防科学技术大学第十八届银河之光文化节ACM程序设计竞赛初赛

题目大意

如题

解题思路

  • 区间dp

  • 状态转移方程:\(dp[i][j]=min(dp[i][j],dp[i][i+k]+dp[i+k+1][j]+sum[j]-sum[i-1])\);

解题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
#define ms(i,a) memset((i),(a),sizeof(i))
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define np next_permutation
#define all(x) x.begin(),x.end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define INF 0x3f3f3f3f

using namespace std;

int n,t,m;

int dp[105][105],sum[105]; //前缀和
void initial(){
rep(i,1,n+1){
rep(j,1,n+1){
if(i==j) dp[i][j]=0;
else dp[i][j]=INF;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n;
sum[0]=0;
rep(i,1,n+1){
cin>>m;
sum[i]=sum[i-1]+m;
}
initial();
for(int len=1; len<n; len++)
{
for(int i=1; i+len<=n; i++)
{
int j=i+len;
for(int k=0; i+k<j; k++)
dp[i][j]=min(dp[i][j],dp[i][i+k]+dp[i+k+1][j]+sum[j]-sum[i-1]);
}
}
cout<<dp[1][n]<<endl;
}
return 0;
}

收获与反思

  • 对于求区间问题最优解,考虑区间dp