石子合并-区间dp

简介: 石子合并-区间dp

题目描述


有 n 堆石子排成一排,每堆石子有一定的数量。现在我们要将 n 堆石子并成为一堆,每次只能合并相邻的两堆石子,合并的花费为这两堆石子的总数。经过 n−1 次合并后会成为一堆,求总的最小花费。


输入描述


第一行输入一个 n ,代表石子的数量。

第二行输入 n 个整数a1,a2,a3...an ,ai 代表第 i 堆石子的数量 。

1<=n<=1000,1<=ai<=10^5


输入输出样例


示例 1

输入

1. 4
2. 4 5 9 4

输出

44

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M


思路


状态定义:定义dp[i][j]为合并区间第i堆到第j堆的最小花费

状态转移:我们采用自顶而下的思想。计算大区间[i,j]的最小值时,合并他的两个子区间[i.k]和[k+1,j]即可,对所有可能的合并(i ≤ k < j,即 k 在 i 、j 之间滑动),返回那个最优的合并。

先初始化dp[i][i]=0,即单个石堆合并无花费,再从区间长度=2开始合并,从小区间合并到大区间,每次增加区间长度。区间为i的状态可以由区间为i-1的状态推导而来。我们得到状态转移方程

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum1[j+1]-sum1[i])

在本体中采用前缀和的方式计算i到j的总的石子数。sum1[j+1]-sum1[i]就是合并当前区间的花费。最终,我们打印输出dp[0,n-1],这就是从第一堆到第n堆的最小花费。


代码


1. n=int(input())
2. s=list(input().split())
3. sum1=[0]*(n+1) #前缀和
4. for i in range(n):
5. sum1[i+1]=sum1[i]+int(s[i])
6. 
7. dp=[[float('inf')]*n for _ in range(n)]
8. for i in range(n):
9.     dp[i][i]=0
10. 
11. for l in range(2,n+1):
12. for i in range(n):
13.         j=i+l-1
14. if j >= n: break
15. for k in range(i,j):
16.             dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum1[j+1]-sum1[i])
17. 
18. print(dp[0][n-1])
目录
相关文章
|
9月前
|
JavaScript
线性dp之石子合并
线性dp之石子合并
|
9月前
|
人工智能 算法
前缀和算法题(区间次方和、小蓝平衡和、大石头的搬运工、最大数组和)
前缀和算法题(区间次方和、小蓝平衡和、大石头的搬运工、最大数组和)
|
人工智能 算法 C++
洛谷 P1090 合并果子(贪心)
洛谷 P1090 合并果子(贪心)
157 0
|
9月前
【每日一题Day192】LC1033移动石子直到连续 | 分类讨论 贪心
【每日一题Day192】LC1033移动石子直到连续 | 分类讨论 贪心
42 0
|
9月前
【每日一题Day223】LC1130叶值的最小代价生成树 | 贪心 区间dp
【每日一题Day223】LC1130叶值的最小代价生成树 | 贪心 区间dp
59 0
|
人工智能
1369:合并果子(fruit)
1369:合并果子(fruit)
119 0
区间DP:合并石子
区间DP:合并石子
71 0
|
人工智能 算法 C++
合并果子。
合并果子。
合并果子。