题目
设有 堆石子排成一排,其编号为 。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
例如有 堆石子分别为 1 3 5 2
, 我们可以先合并 堆,代价为 ,得到 4 5 2
, 又合并 堆,代价为 ,得到 9 2
,再合并得到 ,总代价为 ;
如果第二步是先合并 堆,则代价为 ,得到 4 7
,最后一次合并代价为 ,总代价为 。
问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
输入格式
第一行一个数 表示石子的堆数 。
第二行 个数,表示每堆石子的质量(均不超过 )。
输出格式
输出一个整数,表示最小代价。
数据范围
输入样例:
4
1 3 5 2
输出样例:
22
解题
方法一:动态规划
思路
思维过程:
动态规划:
-
状态定义: 表示把第 石子到第 堆石子合并成一堆的所有方式中所用的最小代价。
-
状态转移方程:
-
初始状态:进行第一次状态转移前,没有任何两堆或两堆以上的石子被合并过,所以合并它们的代价应为正无穷,而单堆石子不需要合并,所以合并它们的代价应为 。也就是说:
- ()
- ()
代码
import java.util.*;
import java.io.*;
public class Main {
static final int INF = 0x3f3f3f3f;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
int[] s = new int[n + 1];
for (int i = 1; i <= n; ++i) {
in.nextToken();
s[i] = s[i - 1] + (int) in.nval;
}
int[][] dp = new int[n + 1][n + 1];
for (int len = 2; len <= n; ++len) {
for (int i = 1; i + len - 1 <= n; ++i) {
int j = i + len - 1;
dp[i][j] = INF;
for (int k = i; k < j; ++k) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + s[j] - s[i - 1]);
}
}
}
System.out.println(dp[1][n]);
}
}
#include <iostream>
using namespace std;
const int N = 310, INF = 0x3f3f3f3f;
int n;
int s[N], dp[N][N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
s[i] = x + s[i - 1];
}
for (int len = 2; len <= n; ++len) {
for (int i = 1; i + len - 1 <= n; ++i) {
int j = i + len - 1;
dp[i][j] = INF;
for (int k = i; k < j; ++k) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + s[j] - s[i - 1]);
}
}
}
printf("%d\n", dp[1][n]);
return 0;
}
评论区