石子合并--线性DP--NOI1995

题目描述.

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

输入格式

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

输出格式

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

输入输出样例
输入 #1
4
4 5 9 4

输出 #1
43
54

这类求最大最小的问题我们很容易想到可以用DP来解决,又因为n≤100所以我们可以用二维数组f[i][j]来做DP
那么比较显然的是,i,j可以被我们用来表示一个区间的左右端点,f[i][j]表示区间合并成一堆石子时的最优解。
那么我们考虑怎么转移
假设我们已经求出了其他区间的最优解,那么合并i到j时,我们只需要枚举在哪个点合并,
设枚举的点为k,则转移方程为
f [ i ] [ j ] = m a x / m i n ( f [ i ] [ k ] + f [ k + 1 ] [ j ] + ∑ i = l n = r a [ i ] ) f[i][j] = max/min(f[i][k] + f[k + 1][j] + \sum_{i=l}^{n = r}a[i]) f[i][j]=max/min(f[i][k]+f[k+1][j]+i=ln=ra[i])

∑ \sum 那部分我们显然可以用前缀和优化一下
然后大小都做一遍就行了

#include<bits/stdc++.h>

#define MAXN 100010
#define N 200
#define ll long long
#define rg register
#define gtc() getchar()
#define INF 0x3f3f3f3f

using namespace std;

template <class T>
inline void read(T &s){
	s = 0; T w = 1, ch = gtc();
	while(!isdigit(ch)){if(ch == '-') w = - 1; ch = gtc();}
	while(isdigit(ch)){s = s * 10 + ch - '0'; ch = gtc();}
	s *= w;
}

int f1[N][N], f2[N][N];
int a[N], sum[N];
int n;
inline int s(int i, int j){return sum[j] - sum[i-1];}

int main()
{
	read(n);
	sum[0] = 0;
	for(int i = 1; i <= n; ++i){
		read(a[i]); a[i + n] = a[i];
	}
	for(int i = 1; i <= 2 * n; ++i) sum[i] = sum[i - 1] + a[i];
	for(int u = 1; u < n; ++u){
		for(int i = 1, j = i + u;(j < n + n) && (i < n + n); ++i, j = i + u){
			f2[i][j] = INF;
			for(int k = i; k < j; ++k){
				f1[i][j] = max(f1[i][j], f1[i][k] + f1[k + 1][j] + s(i, j));
				f2[i][j] = min(f2[i][j], f2[i][k] + f2[k + 1][j] + s(i, j));
			}
		}
	}
	
	int ans1 = 0, ans2 = INF;
	for(int i = 1; i <= n; ++i){
		ans1 = max(ans1, f1[i][i + n - 1]);
		ans2 = min(ans2, f2[i][i + n - 1]);
	}
	
	printf("%d\n%d\n", ans2, ans1);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BIGBIGPPT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值