ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解 P1880 石子合并

2022-06-27 20:31:25  阅读:100  来源: 互联网

标签:int 题解 sum 石子 P1880 read MAXN dp


[NOI1995] 石子合并

题目描述

在一个圆形操场的四周摆放 \(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 当然是不能放过,这里写一下此题的题解。

区间 dp 定义状态这一步还是比较简单的,首先肯定能想到定义一个 \(dp_{l,r}\) 这样的状态表示,然后我们只需要,思考一下如何转移方程就行了。

还是套路的枚举一下间断点在哪里。假如是 \(k\) 。

突然发现我们需要预处理一个数组 \(sum_{l,r}\) 表示 \(l\) 到 \(r\) 中数字的和。这样的话我们就很好办了。反正总共的时间复杂度已经大于 \(\mathbb{O}(n^2)\) 了,那么我们这样预处理也问题不大,反正也不会让时间复杂度再增大到哪里去。当然是可以通过前缀和优化到 \(\mathbb{O}(n)\) 的。

状态转移方程显然为:

这里我们只写求最大值的,最小值的相反,但是要注意处理一下初值,我在这卡了 20min 。
\(dp_{l,r}=max\{dp_{l,k}+dp_{k+1,r}+sum_{l,k}+sum_{k+1,r}\} k \in [l,r)\)
非常显然,把两个东西合并起来所需要的代价加起来就 ok 了。

代码

#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
    t=0; register char ch=getchar(); register int fflag=1;
    while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
    while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=386;
int n,a[MAXN];
int dp[MAXN][MAXN],sum[MAXN][MAXN],maxn,f[MAXN][MAXN],minn=0x3f3f3f3f;
int main(){
	read(n);
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;++i) read(a[i]),a[i+n]=a[i],sum[i][i]=a[i],f[i][i]=0,f[i+n][i+n]=0,sum[i+n][i+n]=a[i];
	for(int len=1;len<=n;++len)
		for(int l=1;l<n*2-len;++l){
			int r=l+len;
			sum[l][r]=sum[l][r-1]+a[r];
		}
	for(int len=1;len<=n;++len)
		for(int l=1;l<n*2-len;++l){
			int r=l+len;
			for(int k=l;k<r;++k){
				f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[l][k]+sum[k+1][r]);
				dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+sum[l][k]+sum[k+1][r]);
			}
		}
	for(int i=1;i<=n;++i) maxn=max(maxn,dp[i][i+n-1]);
	for(int i=1;i<=n;++i) minn=min(minn,f[i][i+n-1]);
	cout<<minn<<endl<<maxn<<endl;
    return 0;
}
//Welcome back,Chtholly.

标签:int,题解,sum,石子,P1880,read,MAXN,dp
来源: https://www.cnblogs.com/Mercury-City/p/16417428.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有