石子合并问题(动态规划)

这篇具有很好参考价值的文章主要介绍了石子合并问题(动态规划)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

石子合并问题是一个经典的动态规划问题,应用了最优子结构和重复子问题的思想。

有如下3种题型:

不加限制的合并

(1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成

(动态规划)O(n^3)
设dp[i][j]表示将i至j之间的石子合并成一堆的最小花费。
初始时,对于任意i,都有dp[i][i]=0,因为合并一堆石子不需要花费。
对于区间[i,j],枚举合并点k,则该区间合并的最小花费为: dp[i][k]+ dp[k+1][j]+sum[i][j],其中 sum[i][j]表示区间[i,j]中石子数量的和。最终答案即为dp[1][n]。

线性(相邻)合并问题

(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

具体思路如下:

  1. 确定状态

设dp[i][j]表示合并第i到j个石子的最小代价。

     2.确定状态转移方程

对于第i到第j个石子的合并,可以选择在任意一个位置k断开,将问题分成合并i到k之间的石子和合并k+1到j之间的石子两个子问题。

因此,可以得到状态转移方程:

dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + sum[i][j]) (i <= k < j)

其中sum[i][j]表示第i到第j个石子的重量和,即需要合并的代价。

      3.确定边界

当只有一个石子时,代价为0,因此dp[i][i] = 0。

      4.最终结果

最终的结果为dp[1][n],表示合并全部石子的最小代价。

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++)
				{
					sum[i][j] = sum[i][k] + sum[k + 1][j];
					dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j]);
				}
			}
		}

我的代码

我在下面的代码中,对sum数组进行了优化(前缀和优化),用s数组表示

#include <iostream> 
#include <cstring> 
using namespace std;
const int N = 310; 
int n; 
int a[N], s[N]; // s数组用于前缀和优化
int f[N][N]; // f[i][j]表示合并第i~j堆石子的最小代价

int main() { 
	cin >> n; 
	for (int i = 1; i <= n; i ++ ) 
		cin >> a[i];

	// 前缀和优化
	for (int i = 1; i <= n; i ++ ) 
		s[i] = s[i - 1] + a[i];
	
	memset(f, 0x3f, sizeof f); // 初值无穷大
	for (int i = 1; i <= n; 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; k < j; k ++ )
	        {
	            f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
	        }
	    }
	}
	
	cout << f[1][n] << endl;	
	return 0;
}

环形合并

(3)问题(2)的是在石子排列是直线情况下的解法,如果把石子改为环形排列,又怎么做呢?

核心思想:

将环形转换为直线
通过将数量变为 2n来转换成直线问题。 比如数组a【1,2,3】,但是环形的要求是1也可以和3连上,所以我们可以把数组a当成 【1,2,3,1,2,3】。这样,我们就可以算出 【2,3,1】的,【3,1,2】的。

我的代码:

#include <iostream> 
#include <cstring> 
using namespace std;
const int N = 310; 
int n; 
int a[2*N+1], s[2*N+1]; // s数组用于前缀和优化
int f[2*N+1][2*N+1]; // f[i][j]表示合并第i~j堆石子的最小代价

int main() { 
	cin >> n; 
	for (int i = 1; i <= n; i ++ ) 
		cin >> a[i];
	for (int i = n + 1; i <= 2 * n; i++)
		a[i] = a[i - n];
	
	// 前缀和优化
	for (int i = 1; i <= 2 * n; i ++ ) 
		s[i] = s[i - 1] + a[i];
	
	memset(f, 0x3f, sizeof f); // 初值无穷大
	for (int i = 1; i <= 2 * n; i ++ ) 
		f[i][i] = 0; // 一堆石子不需要合并
	
	// 枚举区间长度
	for (int len = 2; len <= n; len ++ )
	{
	    // 枚举区间起点
	    for (int i = 1; i + len - 1 <= 2 * n; i ++ )
	    {
	        int j = i + len - 1; // 区间终点
	
	        // 枚举划分位置
	        for (int k = i; k < j; k ++ )
	        {
	            f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
	        }
	    }
	}
	
	cout << f[1][n] << endl;
	return 0;
}

通过上述的状态转移方程,可以将问题分解成两个子问题,并且可以通过最优子结构来推导出最终的结果。同时,由于每个子问题都有重复的子问题,因此可以通过动态规划算法来避免重复计算,提高算法效率。文章来源地址https://www.toymoban.com/news/detail-461954.html

到了这里,关于石子合并问题(动态规划)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 石子合并(动态规划 区间DP)+详细注释

    原题链接   活动 - AcWing 题目 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相

    2024年02月16日
    浏览(38)
  • C++动态规划经典案例解析之合并石子

    区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如 前缀和 就是极简单的区间问题。如有如下数组: 现给定区间信息 [3,6] ,求区间内所有数字相加结果。即求如下图位置数字之和。 Tips: 区间至少包括

    2024年02月11日
    浏览(38)
  • 【算法设计与分析】(三)动态规划_更新中:斐波那契、二项式系数、树的最大独立集、最长递增、公共子序列、编辑距离、Hischberg、最优二叉搜索树、交替拿硬币、石子合并、背包、乘电梯

    分治 动态规划本篇 还差一堆 贪心 网络流 首先,怕误人子弟必须声明一下 本人很菜 (越复习越觉得完蛋了 作为一个科班研究生算法学成这样非常惭愧(跪 ,可能写的都不是很懂,很多内容打算背过去了。因为我发现好像真的有人看所以多提醒一句。。(大家就只食用目录

    2024年01月19日
    浏览(98)
  • 石子合并系列问题

    石子合并问题在网上有三个版本: AcWing 282. 石子合并 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆

    2024年02月02日
    浏览(58)
  • leetcode877. 石子游戏(动态规划-java)

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/stone-game Alice 和 Bob 用几堆石子在做游戏。一共有偶数堆石子,排成一行;每堆都有 正 整数颗石子,数目为 piles[i] 。 游戏以谁手中的石子最多来决出胜负。石子的 总数 是 奇数 ,所以没有平局。 Alice 和 Bob 轮流进行,Alic

    2024年02月10日
    浏览(57)
  • 【动态规划】【C++算法】1563 石子游戏 V

    【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 动态规划汇总 几块石子 排成一行 ,每块石子都有一个关联值,关联值为整数,由数组 stoneValue 给出。 游戏中的每一轮:Alice 会将这行石子分成两个 非空行(即,左侧行和右侧行);Bob 负责计算每一

    2024年02月21日
    浏览(34)
  • 石子合并+环形石子合并+能量项链+凸多边形的划分——区间DP

    一、石子合并 (经典例题) 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合

    2024年02月19日
    浏览(46)
  • 石子合并一章通(环形石子合并,四边形不等式,GarsiaWachs算法)(内附封面)

    在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 试设计出一个算法,计算出将 N N N 堆石子合并成 1 1 1 堆的最小得分和最大得分。 数据的第 1 1 1 行是正

    2024年02月14日
    浏览(39)
  • 石子合并(区间dp)

    (1)f[l][r]表示l~r之间合并的最小代价。 (2)将l~r拆成l~k,k+1~r两份分别最优合并,再把两份合并,对于每个l,r通过枚举所有可能k探寻最优。 (3)最终目标是f[1][n],注意到长区间是由短区间递推出来的,所以由小到大枚举区间长度,再枚举起点,此时l = 起点,r = 起点 +len

    2024年02月10日
    浏览(35)
  • 【动态规划专栏】-- 01 背包问题 -- 动态规划经典题型

    目录 背包问题概述 01 背包问题 01背包⭐⭐  【算法原理】 第一问 第二问 C++ 算法代码 复杂度分析 【空间优化 - 滚动数组】 C++ 算法代码 复杂度分析 分割等和子集⭐⭐ 【算法原理】  对于类01背包问题 C++ 算法代码  【空间优化 - 滚动数组】  C++ 算法代码 目标和⭐⭐ 【算

    2024年02月05日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包