石子合并问题---动态规划

一、问题描述

问题描述:在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选择相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
算法设计:试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
样例输入:由input.txt提供输入数据
样例输出:输出数据保存到output.txt
输入文件示例:input.txt
4
4 4 5 9
输出文件示例:output.txt
43
54

二、问题分析

i表示从第i堆石头开始合并,j表示从i开始数j堆进行合并,0表示最低得分,1表示最高得分
所以m[i][j][0]表示合并的最低得分
同理,m[i][j][1]表示合并的最高得分
由于一开始还没有合并,则m[i][1][0] = m[i][1][1] = 0
sum(i,j)表示从i开始数j个数的和,例如sum(1,2) = a[1]+a[2]
首先两两合并,假设n=5
则m[1][2][0] = m[1][1][0]+m[2][1][0]+sum(1,2)
m[2][2][0] = m[2][1][0]+m[3][1][0]+sum(2,2)

然后三三合并计算m[1][3][0],m[2][3][0]…m[5][3][0]

最后五五合并计算m[1][5][0],m[2][5][0]…m[5][5][0]
所以得出递归方程
m[i][j][0] = min{ m[i][s][0]+m[r][j-s][0]+sum(i,j) } 1≤s<j
其中 r = (i+s-1)%n+1,因为是环形的,所以不能直接写m[i+s][j-s][0]
同理,m[i][j][1] = max{ m[i][s][0]+m[r][j-s]+sum(i,j) } 1≤s<j
边界条件为:m[i][1][0] = m[i][1][1] = 0

三、代码

#include<stdio.h>
#define MaxSize 100
int m[MaxSize+1][MaxSize+1][3];
void MinMax(int i,int j,int n,int s,int a[],int &minf,int &maxf){
 int r = (i+s-1)%n+1;
 int A = m[i][s][0],B = m[r][j-s][0],C = m[i][s][1],D = m[r][j-s][1];
 int sum = 0;
 if(i+j-1 > n){
  for(int k = 1; k <= (i+j-1)%n; k++)
   sum += a[k];
  for(int k = i; k <= n; k++)
   sum += a[k];
 }else
  for(int k = i; k <= i+j-1; k++)
   sum += a[k];
 
  
 minf = A + B + sum;
 maxf = C + D + sum;
}
void solve(int n,int a[]){
 //初始化 
 for(int i = 1; i <= n; i++){
  m[i][1][0] = 0;
  m[i][1][1] = 0;
  for(int j = 2; j <= n; j++){
   m[i][j][0] = 9999;
   m[i][j][1] = 0;
  }
 }
  
 for(int j = 2; j <= n; j++){
  for(int i = 1; i <= n; i++){
   for(int s = 1; s < j; s++){
    int minf,maxf;
    MinMax(i,j,n,s,a,minf,maxf);
    if(minf < m[i][j][0])
     m[i][j][0] = minf;
    if(maxf > m[i][j][1])
     m[i][j][1] = maxf;
   }
  }
 }
 
 int min = 9999,max = 0;
 for(int i = 1; i <= n; i++){
  if(min > m[i][n][0])
   min = m[i][n][0];
  if(max < m[i][n][1])
   max = m[i][n][1];
 }
 printf("\n最小得分:%d\n",min);
 printf("最大得分:%d",max);
}
int main(){
 int n;
 scanf("%d",&n);
 int a[n+1];
 for(int i = 1; i <= n; i++)
  scanf("%d",&a[i]);
 solve(n,a);
 return 0;
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值