有 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)需要花费的最小代价