首页 > 其他分享 >【区间dp】石子合并

【区间dp】石子合并

时间:2024-06-10 22:59:52浏览次数:21  
标签:得分 205 int sum 石子 合并 dp

原题传送门

题目描述

在一个圆形操场的四周摆放 \(N\) 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 \(2\) 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 \(N\) 堆石子合并成 \(1\) 堆的最小得分和最大得分。

输入格式

数据的第 \(1\) 行是正整数 \(N\),表示有 \(N\) 堆石子。

第 \(2\) 行有 \(N\) 个整数,第 \(i\) 个整数 \(a_i\) 表示第 \(i\) 堆石子的个数。

输出格式

输出共 \(2\) 行,第 \(1\) 行为最小得分,第 \(2\) 行为最大得分。

样例 #1

样例输入 #1

4
4 5 9 4

样例输出 #1

43
54

提示

\(1\leq N\leq 100\),\(0\leq a_i\leq 20\)。


思路分析

我们可以使用区间 dp 来解决这道题。我们可以设 \(dp_{l,r}\) 表示合并区间 \((l,r)\) 的石子成一堆所得到的最小得分或最大得分,每一堆肯定是由两堆合并而成的,而这两堆可能是哪些呢?我们可以枚举是哪两堆,枚举中间结点 \(k\),将 \((l,r)\) 区间分割成 \((l,k)\) 和 \((k+1,r)\) 两个子区间,这样我们便得到了一个动态转移方程:

\(dp_{l,r}=\min(dp_{l,r},dp_{l,k}+dp_{k+1,r}+sum_{l,r});\)

\(dp_{l,r}=\max(dp_{l,r},dp_{l,k}+dp_{k+1,r}+sum_{l,r});\)

其中,\(sum_{l,r}\) 的意思是区间 \((l,r)\) 的和,表示本次合并的分数,加入动规数组中,\(dp_{l,k}+dp_{k+1,r}\) 则表示两个子区间的最小或最大得分,最后对两个区间 dp 数组求 \(\min\) 或 \(\max\) 即可。

注意,因为操场是圆形的,我们需要在 \(a\) 数组后再增加一个 \(a\),以方便计算,模拟圆。

具体思路详见注释。

\(\texttt{code}\)

/*Written by smx*/
#include<bits/stdc++.h>
using namespace std;
int n;
int a[205],dpmin[205][205],dpmax[205][205],sum[205];
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	memset(dpmin,0x3f,sizeof(dpmin));//求最小,记得赋最大值
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dpmin[i][i]=0;
		sum[i]=sum[i-1]+a[i];//前缀和
	}
	for(int i=1;i<=n;i++){//增加一倍 a
		dpmin[i+n][i+n]=0;
		sum[i+n]=sum[i+n-1]+a[i];
	}
	for(int len=2;len<=n;len++){//区间长度
		for(int l=1;l+len-1<=2*n;l++){//枚举左端点
			int r=l+len-1;//找到右端点
			for(int k=l;k<r;k++){//枚举中间结点
				dpmin[l][r]=min(dpmin[l][r],dpmin[l][k]+dpmin[k+1][r]+sum[r]-sum[l-1]);
				dpmax[l][r]=max(dpmax[l][r],dpmax[l][k]+dpmax[k+1][r]+sum[r]-sum[l-1]);	//如上动态转移方程
			}
		}
	}
	int ans1=1e9,ans2=0;
	for(int i=1;i<=n;i++){
		ans1=min(ans1,dpmin[i][i+n-1]);
		ans2=max(ans2,dpmax[i][i+n-1]);
	}//每个区间的结果取 min 或 max
	cout<<ans1<<"\n"<<ans2;
	return 0;
}

标签:得分,205,int,sum,石子,合并,dp
From: https://www.cnblogs.com/shimingxin1007/p/18241200

相关文章

  • DP
    ......
  • UDP双向通信
    UDP的双向通信双向交替通信(AlternatingBidirectionalCommunication):在这种方式下,通过约定一方作为发送方,一方作为接收方,双方交替发送和接收数据。例如,一方发送数据报给另一方,然后等待对方的回应,对方接收数据报后进行处理,然后发送回应给发送方,交替进行下去。UDP客户端-服务器通......
  • 斜率优化DP简单总结&&“土地购买”题解
    今天刚刷完了斜率优化DP,简单从头回顾一下。\[首先,能写出DP方程应该是最重要的,毕竟斜率只是用来优化的\]那么一个DP方程能用斜率优化,具备一种形式:\[f[i]+s1[i]+A[i]*B[j]=f[j]+s2[j]\]其中,f[i]表示所求值,(s1[i]、A[i])与(s2[j]、B[j])分别表示只与i或j有关的一个表达式(可以是只有常......
  • WQS 二分 & 凸优化dp
    WQS二分决策单调性,四边形不等式\(O(nk\logn)\toO(n\logn)\)想法转移转成最短路。最短路,转移代价\(\to\)边权。恰好选k条边的最短路为\(f\)。\(f\)必须有凸性。加上额外代价\(\lambda\):\(\lambda\to\inf\),选1边\(\lambda\to-\inf\),选n边二......
  • 【QT5】<总览五> QT多线程、TCP/UDP
    文章目录前言一、QThread多线程二、QT中的TCP编程1.TCP简介2.服务端程序编写3.客户端程序编写4.服务端与客户端测试三、QT中的UDP编程1.UDP简介2.UDP单播与广播程序前言承接【QT5】<总览四>QT常见绘图、图表及动画。若存在版权问题,请联系作者删除!一、QThre......
  • 【NAS】Docker Gitea+SakuraFrp+绿联DPX4800标 搭建私有代码托管平台
    本文主要分享Gitea的一些设置,和Https的实现。Gitea的一些设置映射网络HTTPS的实现先准备好一个域名,建议准备一个1Panel创建一个AC账户然后点击申请证书,手动解析。申请完毕后,点击详情,查看证书crt和私钥key自己创建一个txt文本,将证书crt粘贴进去,然后将名字改为xxx.crt......
  • UDP报文结构
    学习一个协议,首先就是去理解它的报文结构。UDP数据报可以分为报头与载荷两个部分。报头占八个字节,分别是源端口号,目的端口号,udp报文长度,UDP校验和,每个部分占两个字节。载荷是完整的应用层的数据报。报头和载荷可以认为是“拼接“在一起。UDP报文长度:是一个两个字节的16位的......
  • Git:从配置到合并冲突
    目录        1.前言        2.Git的下载与初始化配置        3.Git中新建仓库        4.Git的工作区域和文件状态        5.Git中查看操作和提交记录        6.Git中添加和提交文件        7.Git中回退提交版......
  • (nice!!!)LeetCode 312. 戳气球(区间dp ||记忆化dfs )
    312.戳气球思路:经典区间dp问题。方法一,区间dp。状态dp[i][j]表示:ij这个区间能获得的最大硬币数量。那么我们就可以枚举区间ij的每一个点,为该区间最后一个戳破的气球。细节看注释classSolution{public:intmaxCoins(vector<int>&nums){intn=nums.siz......
  • JAVAEE之网络编程(1)_套接字、UDP数据报套接字编程及从代码实例
    前言什么是网络编程呢? 网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。当然,即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程一、网路编程中的基本概念1.1发送端和接收端发送端:数据的发送方进程,称为发送端。发......