动态规划———区间dp

博客介绍了如何使用区间动态规划解决石子合并问题。通过分析NOI 1995年的竞赛题目,阐述了将圆形操场上的石子合并成一堆时,求解最小得分和最大得分的动态规划方法。重点在于理解如何利用前缀和和动态转移方程找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

区间dp:

首先引出例题:NOI 1995 石子合并

题目描述

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

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

输入输出格式

输入格式: 数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:输出共2行,第1行为最小得分,第2行为最大得分.

输入样例:                                                 输出样例:

4                                                                 43

4 5 9 4                                                        54

乍一看,还以为是贪心。当然有一些贪心思路,但都不是正解。这里正解为区间dp:

dpmax[i][j]表示区间内合并石子的 最大值, 然后我们就可以枚举 断点 k 。因为此题为圆形操场,环状上枚举断点。既然这样,我们就需要将这一圈石子分割。很显然,我们需要枚举一个k,来作为这一圈石子的分割线。

因为只能合并 相邻 的石子,也就是分值只和连续几堆石子数量的和有关,很容易想到用 前缀和 来实现: sum[i] 表示前 i 堆石子数量和。

由于可以头尾合并,于是我们就要多开 n 个空间,也要多循环 n 次。然后枚举 区间头端 ,一个个区间地找 最大值/最小值 ,最后输出即可。

于是我们得到动态转移方程:

f[i][j] = max(f[i][k] + f[k+1][j] + sum(i,j));其中,1<=i<=<=k<j<=N。

那么接下来就是考虑递推过程了:

我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态。

但这样i和j就难以确定了。所以我们需要枚举j-i,并在j-i中枚举k。——————摘自TIMI_k的方法、

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm> 
using namespace std;
const int maxn=1e10;
int n,ans2,ans1,dp1[1001][1001],dp2[1001][1001],num[1001];
int s[1001];//连缀和数组 
inline int sum(int i,int j){return s[j]-s[i-1];}//分数为当前石头数量即为连缀和之差 
int main(){+
	cin>>n;
	for(int i=1;i<=n+n;i++){
		cin>>num[i];
		num[i+n]=num[i];
		s[i]=s[i-1]+num[i];//求连缀和过程Q 
	}
	for(int p=1;p<n;p++){
		for(int i=1,j=i+p;(j<n+n)&&(i<n+n);i++,j=i+p){
			dp2[i][j]=maxn;//求min初始化 
			for(int k=i;k<j;k++){
				dp1[i][j]=max(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum(i,j));
				dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum(i,j));
			}
		}
	}
	ans2=maxn;
	for(int i=1;i<=n;i++){
		ans1=max(ans1,dp1[i][i+n-1]);
		ans2=min(ans2,dp2[i][i+n-1]);
	}//寻找得分最小and最大 
	cout<<ans2<<endl<<ans1; 
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值