Pangu and Stones(The 2017 ACM-ICPC Asia Beijing Regional J 区间dp变形)

题目链接:

J - Pangu and Stones

 

题意:

有 n 堆石子,每堆有 a[i] 个,给定L,R,相邻的连续 [L,R] 堆石子可以合并成一堆,代价是这些堆中石子数量之和。求把 n 堆石子合并成一堆的最小代价,情况不存在输出 0 。

 

思路:

若L=R=2,则就是传统的区间dp:

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

dp[i][j] 表示第 i 堆石子到第 j 堆石子合并成一堆的最小代价。

现在多了一个限制条件,L,R不确定。所以我们在原先 dp 数组中再添一维。

dp[i][j][k] 表示第 i 堆石子到第 j 堆石子合并成 k 堆的最小代价

k=1时:dp[i][j][1] = min(dp[i][j][1],dp[i][t][x-1]+dp[t+1][j][1]+sum[i...j])    (1<=t<j,L<=x<=R)

k>1时:dp[i][j][k] = min(dp[i][j][k],dp[i][t][k-1]+dp[t+1][j][1])   (1<=t<j,2<=k<=t-i+2(即k-1要小于等于子区间[i,t]的长度))

(两者计算顺序没有关系,彼此都不会用到相关数据)

初始化:由于要求最小值,所以需要先把 dp 数组初始化为 inf 。

然后,把 k 堆石子分成 k 堆的代价为 0 (相当于不分),即 dp[i][j][j-i+1]=0 。

Tips:计算 k>1 的情况时不需要满足 L<=k<=R 的性质。因为这些情况都是由 k=1 的情况转移而来,并且在 k=1 的时候已经限制好了( dp[i][j][1]=inf 时 ,该情况不会再发生转移 )。因此这些状态都是合法状态。

 

Code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MAX = 100 + 10;
const ll inf = 1e18;

int n, l, r;
ll a[MAX];
ll sum[MAX];
ll dp[MAX][MAX][MAX];

int main()
{
	while (scanf("%d%d%d", &n, &l, &r) != EOF)
	{
		for (int i = 1; i <= n; i++) {
			scanf("%lld", &a[i]);
		}
		for (int i = 1; i <= n; i++) {
			sum[i] = sum[i - 1] + a[i];
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				for (int k = 1; k <= n; k++) {
					dp[i][j][k] = inf;
				}
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = i; j <= n; j++) {
				dp[i][j][j - i + 1] = 0;
			}
		}
		for (int len = 2; len <= n; len++) {
			for (int i = 1; i <= n; i++) {
				int j = i + len - 1;
				if (j > n)	break;
				for (int t = i; t < j; t++) {
					for (int k = l; k <= r; k++) {
						dp[i][j][1] = min(dp[i][j][1], dp[i][t][k - 1] + dp[t + 1][j][1] + sum[j] - sum[i - 1]);
					}
				}
				for (int t = i; t < j; t++) {
					for (int k = 2; k <= t - i + 2; k++) {
						dp[i][j][k] = min(dp[i][j][k], dp[i][t][k - 1] + dp[t + 1][j][1]);
					}
				}
			}
		}
		ll ans = dp[1][n][1];
		if (ans == inf) {
			printf("0\n");
		}
		else {
			printf("%lld\n", ans);
		}
	}
	return 0;
}

/*
5 3 3
1 2 1 3 5
5 2 2
1 2 1 3 5
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值