有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。
其中,1 <= N <= 30,1 <= C[i] <= 100
有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。
其中,1 <= N <= 30,1 <= C[i] <= 100
第一行输入一个数字 N 表示有 N 堆金币第二行输入 N 个数字表示每堆金币的数量 C[i]
输出一个数字 S 表示最小的合并成一堆的成本
4 3 2 4 1
20
30 10 20 30 40 50 60 70 80 90 100 99 89 79 69 59 49 39 29 19 9 2 12 22 32 42 52 62 72 82 92
7307
/**手动模拟最小情况,发现关键就是在确定区间,确定分割点*/ 已AC
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] money = new int[n+1];
int[] preSum = new int[n+1];
for(int i = 1; i <= n; i++){
money[i] = sc.nextInt();
if(i == 1) preSum[i] = money[i];
else preSum[i] = preSum[i-1] + money[i];
}
sc.close();
int[][] dp = new int[n + 1][n + 1];
for(int len = 2; len <= n; len++){
for(int i = 1; i <= n - len + 1; i++){
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;
int sum = preSum[j] - preSum[i - 1];
for(int k = i; k < j; k++){
dp[i][j] = Math.min(dp[i][j],dp[i][k] + dp[k + 1][j] + sum);
}
}
}
System.out.println(dp[1][n]);
}
}
区间DP + 前缀和
#include <bits/stdc++.h>
using namespace std;
const int N = 31;
int a[N], s[N];
int f[N][N];
int n;
int main(){
memset(f, 0x3f, sizeof f);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], s[i] = s[i - 1] + a[i], f[i][i] = 0; // 初始化
for (int len = 2; len <= n; len++){//枚举区间长度
for (int i = 1; i + len - 1 <= n; i++){//枚举起点
int j = i + len - 1; // 终点
for (int k = i + 1; k <= j; k++){ // 计算[i, j]最小代价
f[i][j] = min(f[i][j], f[i][k - 1] + f[k][j] + s[j] - s[i - 1]);
}
}
}
cout << f[1][n] << endl;
return 0;
}
public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int[] jinbi = new int[n]; int[] sum = new int[n]; for(int i=0;i<n;i++) { jinbi[i] = in.nextInt(); if(i==0) sum[i]=jinbi[i]; else sum[i]=sum[i-1]+jinbi[i]; } in.close(); long temp = 0; long min =0; long[][] dp = new long[n][n]; for(int l=1;l<n;l++) { for(int i=0;i<n&&i+l<n;i++) { min = dp[i][i]+dp[i+1][i+l]; for(int k=i+1;k<=i+l-1;k++) { temp = dp[i][k] + dp[k+1][i+l]; if(temp<min) min = temp; } if(i>0) dp[i][i+l]=min+sum[i+l]-sum[i-1]; else dp[i][i+l]=min+sum[l]; } } System.out.println(dp[0][n-1]); for(int i =0;i <n;i++){ for(int j = 0;j<n;j++){ System.out.print(dp[i][j] + " "); } System.out.println(); } }
N = int(input())
C = list(map(int,input().split(' ')))
dp = [[0 for _ in range(N)] for _ in range(N)]
for i in range(N-1):
dp[i][i+1] = C[i]+C[i+1]
for i in range(N-1)[::-1]:
for j in range(i+2, N):
dp[i][j] = dp[i][i] + dp[i+1][j] + sum(C[i:j+1])
for k in range(i+1, j):
dp[i][j] = min(dp[i][k]+dp[k+1][j] + sum(C[i:j+1]), dp[i][j])
print(dp[0][-1])
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] arr = new int[n+1];
int[] pre = new int[n+1];
for (int i = 1; i <= n; i++) {
arr[i] = in.nextInt();
pre[i] = pre[i-1] + arr[i];
}
fun(arr, pre, n);
in.close();
}
// 合并金币-动态规划
// dp[i][j] 表示第i堆到第j堆合并的代价和
// base case: dp[x][x] = 0
// 方程:dp[i][j] = dp[i][k]+dp[k+1][j]+cost
// 计算顺序: 从下到上 从左到右
public static void fun(int[] arr, int[] pre, int n) {
int[][] dp = new int[n+1][n+1];
// i是左边界 j是右边界 k是分割点
// 注意枚举时的取值问题
for (int i = n; i > 0; i--) {
for (int j = i+1; j <= n; j++) {
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = pre[j] - pre[i-1];
dp[i][j] = Math.min(dp[i][k] + dp[k+1][j] + cost, dp[i][j]);
}
}
}
System.out.println(dp[1][n]); // 结果是从1合并到n的代价
}
}
#include<iostream>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
const int size = n;
vector<int> C(size);
int x;
for(int i=0; i<size; ++i)
{
cin>>x;
C[i] = x;
}
if(size == 1) { cout<<0; return 0; }
vector<vector<int>> dp(size, vector<int>(size, 0));
for(int i=size-1; i>=0; --i)
{
for(int j=i+1; j<size; ++j)
{
int _min = INT_MAX;
for(int k=i; k<j; ++k)
{
_min = min(_min, dp[i][k]+dp[k+1][j]);
}
for(int p=i; p<=j; ++p) {dp[i][j]+=C[p];}
dp[i][j] += _min;
}
}
cout << dp[0][size-1] << endl;
return 0;
} //动态规划
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] stones = new int[n];
for(int i = 0; i < n; i++){
stones[i] = sc.nextInt();
}
sc.close();
//动态规划dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i]);
int[][] dp=new int[stones.length][stones.length];
int[] sum=new int[stones.length+1];
for(int i=1;i<stones.length+1;i++){
sum[i]=sum[i-1]+stones[i-1];
//dp[i-1][i-1]=stones[i-1];
}
for(int i=stones.length-1;i>=0;i--){
for(int j=i+1;j<stones.length;j++){
dp[i][j]=Integer.MAX_VALUE;
for(int k=i;k<j;k++){
dp[i][j]=Math.min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j+1]-sum[i]);
}
}
}
System.out.println(dp[0][n-1]);
}
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine());
int[] arr = new int[n];
for (int i = 0; i < n ; i++) {
arr[i] = scanner.nextInt();
}
System.out.println(minCombination(arr));
}
private static int minCombination(int[] arr) {
int n = arr.length;
int[][] sum = new int[n + 1][n + 1];
int[][] dp = new int[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
dp[i][i] = 0;
sum[i][i] = arr[i - 1];
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
sum[i][j] = sum[i][j - 1] + sum[j][j];
}
}
for (int len = 1; len < n; len++) { //区间长度
for (int i = 1; i + len <= n; i++) { //区间开始
int j = i + len; //区间结尾
for (int k = i; k < j; k++) {
if(dp[i][j] == 0){ //确保dp[i][j]的更新
dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[i][j];
}else{
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j]);
}
}
}
}
return dp[1][n];
}
}
length = int(input())
coins = list(map(int, input().split()))
dp = [[float("inf")] * length for _ in range(length)]
for i in range(length - 1, -1, -1):
for j in range(i, length):
if i == j:
dp[i][j] = 0
else:
curSum = sum(coins[i: j + 1])
for k in range(i, j):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + curSum)
print(dp[0][-1]) #include <iostream>
#include <algorithm>
#include <vector>
#include <limits.h>
class Solution{
public:
template<typename T> using Matrix = std::vector<std::vector<T>>;
static void solve(const std::vector<int>& data, int length) {
int N = length;
Matrix<int> dp(N+1, std::vector<int>(N+1, INT_MAX));
Matrix<int> sum(N+1, std::vector<int>(N+1));
// 初始化,basecase
for(int i=1; i <= N; ++i) {
sum[i][i] = data[i];
dp[i][i] = 0;
}
for(int interval=1; interval < N; ++interval) { // 区间长度
// 对于同一个区间长度,不同起点,寻找最小的相邻的两堆
// 区间最大的终点是数组的最后一个元素 index:N
for(int begin=1; begin + interval <= N; ++begin) { // 区间起点
int end = begin + interval; // 区间终点
/*** @brief: 思路,在区间[begin, end]找到和最小的两个,加起来,作为 dp[begin][end]的值
*
*
* 动态方程解释:dp[begin][k] + dp[k+1][end] + sum[begin][end]
* 因为之前合并成 [begin][k]、[k+1][end]这两堆所需的代价,
* 将他们合并成新的堆,所需的代价,就需要在此基础上加上新的和: sum[begin][end]
*
* dp[begin][end] = std::min(dp[begin][end], dp[begin][k] + dp[k+1][end] + sum[begin][end]);
*
* 就是为了找个最小值:相同的区间长度,不同的起点。
*
*/
for(int k=begin; k < end; ++k) {
sum[begin][end] = sum[begin][k] + sum[k+1][end];
dp[begin][end] = std::min(dp[begin][end], dp[begin][k] + dp[k+1][end] + sum[begin][end]);
}
}
}
std::cout<< dp[1][N]<<std::endl;
}
};
int main(int argc, char const *argv[]) {
int N;
std::cin>>N;
std::vector<int> data(N+1, 0);
for(int i =1; i <= N; ++i) {
std::cin>> data[i];
}
Solution::solve(data, N);
return 0;
}
#include<vector>
(721)#include<iostream>
#include<algorithm>
using namespace std;
int minGold(const vector<int>golds) {
if (golds.size() == 1)return 0;
vector<int>sums(golds.size());
for (int i = 0; i < golds.size(); ++i) {
sums[i] = golds[i];
if (i != 0)
sums[i] += sums[i - 1];
}
vector<vector<int>>dp;
for (int i = 0; i < golds.size(); ++i) {
dp.emplace_back(golds.size(), 0);
}
for (int i = golds.size()-1; i >=0; --i) {
for (int j = i; j < golds.size(); ++j) {
if (i == j)
dp[i][j] =0;
else{
int tmp = INT32_MAX;
for (int k = i; k < j; ++k) {
tmp = min(tmp, dp[i][k] + dp[k + 1][j] + sums[j] - sums[i] + golds[i]);
}
dp[i][j] = tmp;
}
}
}
return dp[0][golds.size()-1];
}
int main() {
int num;
cin >> num;
vector<int>nums(num);
for (int i = 0; i < num; ++i)
cin >> nums[i];
cout << minGold(nums) << endl;
} import java.util.Scanner;
public class Main {
public static void main(String[] args) {
mergeCoins();
}
private static void mergeCoins() {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] arr = new int[n];
int[] sum = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = scanner.nextInt();
sum[i] = i == 0 ? arr[i] : arr[i] + sum[i - 1];
}
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
if(i == j)
dp[i][j] = 0;
else if(i + 1 == j)
dp[i][j] = arr[i] + arr[j];
else {
dp[i][j] = Integer.MAX_VALUE;
int sumIJ = i == 0 ? sum[j] : sum[j] - sum[i - 1];
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + sumIJ);
}
}
}
}
System.out.println(dp[0][n - 1]);
}
}
dp[i][j]: 合并 [i, j] 间所有的堆(包括堆 i 与堆 j)需要花费的最小代价