当前位置: 代码迷 >> 综合 >> 石子合并问题(no circle)
  详细解决方案

石子合并问题(no circle)

热度:36   发布时间:2023-11-29 20:48:35.0

算法实现题 3-5 石子合并问题(区间DP)
题目地址
题目描述
桌面上从左到右放着 n(1≤n≤200) 堆石子,其中第 i堆石子包含的石子数量为 ai
现在要将石子有序地合并成一堆。
规定每次只能取相邻的两堆石子合并成新的一堆,并将新的一堆的石子数,记为该次合并的花费。
那么,n?1 次合并后,石子将合并成一堆。
你需要寻找一种合并方案,使得花费总和最小。输出最小的花费总和。

输入格式:
输入的第一行包含一个整数 n(1≤n≤200),用于表示石子堆数。
输入的第二行包含 n 个整数,以空格间隔,分别表示初始时每一堆的石子数。

输出格式:
输出一个整数,用于表示将 n 堆石子合并成一堆的最小花费。

输入输出样例
输入

5
1 3 2 4 5

输出

34

算法分析:
对于动态规划问题首先找出它的状态转移方程:
对于最小得分:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]) 条件:j!=i
对于最大得分:
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]) 条件:j!=i
对于i=j时 其实就相当于就一堆 dp[i][j]=0;
方程解释:
dp[i][j]: 合并第i堆石子到第j队石子所用的最大(小)得分或者说合并[i,j]这个区间范围内的石子最大/小花费总和
sum数组是前缀和,sum[i]表示一个数组从下标为0到下标为i之间所有的数据和
sum[i]=sum[i-1]+a[i]
想要更详细的请参考这位大哥?前缀和?

dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1] 其中的k参数代表从i到j的上一次合并是从k分开的,也就是上一步状态是从i->k,k+1->j(不懂看下图)合并成一堆在加上a[i]+a[i+1]+a[i+2]···a[j]用前缀和表示就是sum[j]-sum[i-1]
在这里插入图片描述在这里插入图片描述
在这里插入图片描述dp[i][j]像这种每个状态都对应一个区间这类问题有专门的称呼---->区间DP
可以通过区间短的状态推导出区间长的状态//决策单调性
在这里插入图片描述从len=1的f[i][i]可以推出len=2的f[i][i+1]

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=220;
int n ,a[maxn],sum[maxn],f[maxn][maxn];
int main()
{cin>>n;for(int i=1;i<=n;i++){scanf("%d",a+i);sum[i]=sum[i-1]+a[i];}memset(f,INF,sizeof(f));for(int len=1;len<=n;len++)//区间长度{for(int i=1;i+len-1<=n;i++)//[i,j]区间起点终点{int j=i+len-1;//终点if(len==1)f[i][j]=0;else{for(int k=i;k<j;k++)f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);}}}cout<<f[1][n]<<endl;
}

参考?b站+本站+启蒙篇

  相关解决方案