[51nod - 1021] 石子归并 (区间dp)

链接

https://www.51nod.com/Challenge/Problem.html#problemId=1021

题意

  有 N N N堆石子排成一排,其中第 i i i堆石子的质量为 A i A_i Ai,每次都可以选择其中相邻的两堆石子合并成一堆,形成的新石子堆的重量以及消耗的体力都是两堆石子的重量之和。求把全部 N N N堆石子合成一堆最少需要消耗多少体力。 1 ≤ N ≤ 300 1\leq N\leq 300 1N300

分析

  这是一个区间 d p dp dp入门题目。
  若最初的第 l l l堆石子和第 r r r堆石子被合并成一堆,则说明 l l l~ r r r之间的每堆石子也已经被合并,这样 l l l r r r才有可能相邻。因此,在任意时刻,任意一堆石子均可以用一个闭区间 [ l ,   r ] [l,\ r] [l, r]来描述,表示这堆石子是由最初的第 l l l~ r r r堆石子合并而成的,其重量为 ∑ i = l r A i \sum_{i=l}^r A_i i=lrAi。另外一定存在一个整数 k ( l ≤ k &lt; r ) k(l\leq k&lt;r) k(lk<r),在这堆石子形成之前,现有第 l l l~ k k k堆石子(闭区间 [ l ,   r ] [l,\ r] [l, r])被合并成一堆,第 k + 1 k+1 k+1~ r r r堆石子(闭区间 [ k + 1 ,   r ] [k+1,\ r] [k+1, r])被合并成一堆,然后这两堆石子才合并成 [ l ,   r ] [l,\ r] [l, r]
  对应到动态规划中,就意味着两个长度较小的区间上的信息向一个更长的区间发生了转移,划分点 k k k就是转移的决策,自然地,应该把区间 l e n len len作为 D P DP DP的阶段,区间长度可以由左右端点表示出,我们可以使用左右端点表示 D P DP DP的状态。
   d p [ l ,   r ] dp[l,\ r] dp[l, r]表示把最初的第 l l l堆到第 r r r堆合并到一堆,需要消耗的最少体力。状态转移方程如下: d p [ l ,   r ] = min ⁡ l ≤ k &lt; r { d p [ l ,   k ] + d p [ k + 1 ,   r ] } + ∑ i = l r A i dp[l,\ r]=\displaystyle \min_{l\leq k&lt;r}\{dp[l,\ k]+dp[k+1,\ r]\}+\sum_{i=l}^rA_i dp[l, r]=lk<rmin{dp[l, k]+dp[k+1, r]}+i=lrAi
   d p [ l ] [ l ] = A l dp[l][l]=A_l dp[l][l]=Al,其余为 ∞ \infty ,最终结果为 d p [ 1 ,   N ] dp[1,\ N] dp[1, N],对于 ∑ i = l r A i \sum_{i=l}^rA_i i=lrAi可用前缀和计算。

代码
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>

#define INF 0x7f7f7f7f
#define MAXN 100005
#define N 200005
#define P 2
#define MOD 99991

typedef long long ll;

namespace fastIO {
	//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
	//char buf[(1 << 22)], *p1 = buf, *p2 = buf;
	inline int read() {
		char c = getchar(); int x = 0, f = 1;
		while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
		while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
		return x * f;
	}
}

using namespace fastIO;
using namespace std;

int n, m, dp[305][305], A[450];

int main() {
	cin >> n;
	int x;
	memset(dp, INF, sizeof(dp));
	for (int i = 1; i <= n; i++) {
		cin >> x;
		A[i] = A[i - 1] + x;
		dp[i][i] = 0;
	}
	for (int len = 1; len <= n; len++) {
		for (int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			for (int k = l; k < r; k++)
				dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + A[r] - A[l - 1]);
		}
	}
	cout << dp[1][n] << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值