赞
踩
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请设计一个程序,计算出将N堆石子合并成一堆的最小得分和最大得分。
我们对n的取值逐步分析
当n=1时,没有进行合并,得分为0
当n=2时,只有一种合并的方案,得分为两堆石子之和
当n=3时,有两种得分方案,先合并左边两个,先合并右边两个,这两种方案的区别我们可以看成是起点不同,最后一次合并的得分都是这三堆石子的总数
当n=4时,有三种得分方式,分别是先合并后面三个,先合并后面两个,先合并后面一个(也就是先合并前面三个),这三种方案对应的起点是1、2、3,最后一次合并的得分都是这四堆石子的总数。
由此可知,我们所求的最大或者最小的得分就是这几种方案中得分最大或者最小的分数加上最n堆石子的总石子数,可得dp方程为:
存在k属于区间[i,j]
最大得分:dp[i][j] = max(dp[i][k] +dp[k+1][j]) + sum[i][j]
最小得分:dp[i][j] = min(dp[i][k] +dp[k+1][j]) + sum[i][j]
举例:
代码实现:
- #include <iostream>
- #include <math.h>
- using namespace std;
-
- void fun(int c[],int n){
- //计算sum[][]
- int sum[100][100] = {0};
- for(int i = 0;i<n;i++){
- for(int j = 0;j<n;j++){
- if(i == j){
- sum[i][j] = c[j];
- }else if(j>i){
- sum[i][j] = c[j] + sum[i][j-1];
- }
- }
- }
- //准备两个二维数组分别用于求最大得分和最小得分
- int dp1[100][100] = {0};
- int dp2[100][100] = {0};
- for(int len = 2;len<=n;len++){ //枚举区间
- for(int i = 0;i < n-len+1;i++){ //枚举此区间的每一个起点,最大的起点是整排石子的长度从后面往前面减当前长度加一
- //定义终点
- int j = i + len -1;
- dp1[i][j] = 1e8;
- dp2[i][j] = -1;
- for(int k = i;k<j;k++){ //从起点到终点一直遍历k,寻找最大最小得分·
- dp1[i][j] = min(dp1[i][j],dp1[i][k] + dp1[k+1][j] + sum[i][j]);
- dp2[i][j] = max(dp2[i][j],dp2[i][k] + dp2[k+1][j] + sum[i][j]);
- }
- }
- }
- cout<<dp1[0][n-1]<<endl;
- cout<<dp2[0][n-1]<<endl;
- }
-
- int main(){
- int n;
- cin>>n;
- int c[100];
- for(int i = 0;i<n;i++){
- cin>>c[i];
- }
- fun(c,n);
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。