石子合并——区间dp
石子合并(3种变形)
<1>
题目:
有N
堆石子排成一排(n<=100
),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n
及每堆石子数(<=200
);
(1)选择一种合并石子的方案,使得做n-1
次合并,得分的总和最少;
(2)选择一种合并石子的方案,使得做n-1
次合并,得分的总和最多;
输入格式
第一行为石子堆数n
第二行为每堆石子数,每两个数之间用一空格分隔。
输出格式
从第1
行为得分最小
第2
行是得分最大。
样例
样例输入
4
4 5 9 4
样例输出
44
54
特点:
仅是一排石子,而且只能是相邻两个石子相邻可以合并,所以第1
个石子只能与右边的合并,第n
个石子只能与左边的合并,所以用区间dp简单枚举合并即可。
数组表示:(以下同)
f[i][j]
:从i
到j
的区间的最大/最小值;
sum[i]
:i
的前缀和(便于计算);
动态规划:
阶段:
区间长度d
,d=1
时初始值即石子本身分数,所以从d=2
开始枚举,以此到d=n
;
状态:
从i
到j
的区间的最大/最小值;
决策:
是否将从i
到k
的区间与从k+1
到j
的区间合并,若合并即加上sum[i]-sum[j-1]
(i
到j
的分数);
动态转移方程:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[i]-sum[j-1]);
f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[i]-sum[j-1]);
注意:
最小值f[i][j]
的初始值为0x7f7f7f7f
;
最大值f[i][j]
的初始值为0
;
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=+;
const int INF=0x3f3f3f3f;
int n;
int f1[maxn][maxn],f2[maxn][maxn];
int sum[maxn];
int a[maxn];
int minx=INF,maxx=;
void MIN(){
for(int d=;d<=n;d++){
for(int i=,j;(j=i+d-)<=n;i++){
for(int k=i;k<j;k++){
f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+][j]+sum[j]-sum[i-]);
}
}
}
}
void MAX(){
for(int d=;d<=n;d++){
for(int i=,j;(j=i+d-)<=n;i++){
for(int k=i;k<j;k++){
f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+][j]+sum[j]-sum[i-]);
}
}
}
}
int main(){
memset(f1,INF,sizeof(f1));
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=;i<=n;i++){
if(i==){
sum[i]=a[i];
}else{
for(int j=;j<=i;j++){
sum[i]+=a[j];
}
}
}
for(int i=;i<=n;i++){
f1[i][i]=;
}
MIN();
MAX();
printf("%d\n",f1[][n]);
printf("%d\n",f2[][n]);
return ;
}
<2>
题目:
在一个园形操场的四周摆放N
堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2
堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1
个算法,计算出将N
堆石子合并成1
堆的最小得分和最大得分。
输入格式
数据的第1
行试正整数N,(1≤ N ≤100)
,表示有N
堆石子。
第2
行有N
个数,分别表示每堆石子的个数。
输出格式
输出共2
行,第1
行为最小得分,第2
行为最大得分。
样例
样例输入
4
4 4 5 9
样例输出
43
54
特点:
与上一题不同的是,这里是圆形操场,即现在的石子是一个环,现在第1
个石子与第n
个石子算是相邻,也是很简单,自己将数组存储两边石子的分数,便可以模拟环,然后直接按上次的dp处理即可。
注意:
此时的d
还是枚举到n
;
而i
和j
则需要枚举到2*n
;
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=+;
const int INF=0x3f3f3f3f;
int n;
int f1[maxn][maxn],f2[maxn][maxn];
int sum[maxn];
int a[maxn];
int minx=INF,maxx=;
void MIN(){
for(int d=;d<=n;d++){
for(int i=,j;(j=i+d-)<=*n;i++){
f1[i][j]=INF;
for(int k=i;k<j;k++){
f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+][j]+sum[j]-sum[i-]);
}
}
}
}
void MAX(){
for(int d=;d<=n;d++){
for(int i=,j;(j=i+d-)<=*n;i++){
f2[i][j]=;
for(int k=i;k<j;k++){
f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+][j]+sum[j]-sum[i-]);
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
a[n+i]=a[i];
}
for(int i=;i<=*n;i++){
sum[i]=sum[i-]+a[i];
}
MIN();
MAX();
int zuix=INF,zuid=;
for(int i=;i<=n;i++){
zuix=min(zuix,f1[i][i+n-]);
zuid=max(zuid,f2[i][i+n-]);
}
printf("%d\n%d\n",zuix,zuid);
return ;
}
<3>
题目:
在一个园形操场的四周摆放N
堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2
堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1
个算法,计算出将N
堆石子合并成1
堆最大得分。
输入格式
数据的第1
行试正整数N,1≤N≤2000
,表示有N
堆石子.第2
行有N
个数,分别表示每堆石子的个数。
输出格式
输出共1
行,最大得分。
样例
样例输入
4
4 4 5 9
样例输出
54
注意:
虽然一看这题与<2>
完全一样,但是仔细看的话,N
的范围发生了变化,那么就不能与<1>、<2>
一样用直接dp的方法,上两次只有N3的效率,所以这次得需要优化。
区间dp的优化方式有四边不等式法,但是这里是求最大值,没有单调性,无法用四边不等式。
但是最大值有一个性质,总是在两个端点的最大者中取到。
动态转移方程式:
f[i][j]=max(f[i][j-1],f[i+1][j])+sum[j]-sum[i-1]
;
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+,INF=0x3f3f3f3f;
int n,m,f[][],ans,a[maxn],sum[];
int main(){
cin>>n;
for(int i=;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
sum[i]=a[i]+sum[i-];
}
for(int i=n+;i<=*n;i++){
sum[i]=sum[i-]+a[i];
}
for(int d=;d<=n;d++){
for(int i=,j;(j=i+d-)<=n*;i++){
f[i][j]=max(f[i][j-],f[i+][j])+sum[j]-sum[i-];
}
}
int ansmax=;
for(int i=;i<=n;i++){
ansmax=max(ansmax,f[i][i+n-]);
}
cout<<ansmax<<endl;
return ;
}
石子合并——区间dp的更多相关文章
- 洛谷 P1880 [NOI1995] 石子合并(区间DP)
传送门 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 这道题是石子合并问题稍微升级版 这道题和经典石子合并问题的不同在于,经典的石子合 ...
- 石子合并 区间dp模板
题意:中文题 Description 在操场上沿一直线排列着 n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分.允许在第一次合 ...
- HDU4632 Poj2955 括号匹配 整数划分 P1880 [NOI1995]石子合并 区间DP总结
题意:给定一个字符串 输出回文子序列的个数 一个字符也算一个回文 很明显的区间dp 就是要往区间小的压缩! #include<bits/stdc++.h> using namesp ...
- 石子合并 区间DP模板题
题目链接:https://vjudge.net/problem/51Nod-1021 题意 N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石 ...
- 洛谷 P1080 石子合并 ( 区间DP )
题意 : 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.试设计出1个算法,计算出将N堆石子合并成1堆 ...
- 洛谷P1880 石子合并(环形石子合并 区间DP)
题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...
- HRBUST - 1818 石子合并 区间dp入门
有点理解了进阶指南上说的”阶段,状态和决策“ /* 区间dp的基础题: 以区间长度[2,n]为阶段,枚举该长度的区间,状态dp[l][r]表示合并区间[l,r]的最小费用 状态转移方程dp[l][r] ...
- HDU 3506 (环形石子合并)区间dp+四边形优化
Monkey Party Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Tot ...
- P1880 [NOI1995]石子合并 区间dp+拆环成链
思路 :一道经典的区间dp 唯一不同的时候 终点和起点相连 所以要拆环成链 只需要把1-n的数组在n+1-2*n复制一遍就行了 #include<bits/stdc++.h> usi ...
随机推荐
- java实现第五届蓝桥杯写日志
写日志 写日志是程序的常见任务.现在要求在 t1.log, t2.log, t3.log 三个文件间轮流 写入日志.也就是说第一次写入t1.log,第二次写入t2.log,... 第四次仍然 写入t1 ...
- SpringSecurity(2)---记住我功能实现
SpringSecurity(2)---记住我功能实现 上一篇博客实现了认证+授权的基本功能,这里在这个基础上,添加一个 记住我的功能. 上一篇博客地址:SpringSecurity(1)---认证+ ...
- sql server 连接种类
一.连接种类 内连接 inner join 如果分步骤理解的话,内连接可以看做先对两个表进行了交叉连接后,再通过加上限制条件(SQL中通过关键字on)剔除不符合条件的行的子集,得到的结果就是内连接了. ...
- java作品集:企业信息门户webtap
作品背景 随着企业应用的软件越来越多,并且信息软件基本以B/S为主了,很多时候各种软件的地址,让大家记的头昏脑胀,并且一堆密码要记,而且大部分系统之间无法互通,虽然市面上有各种集成方案,但无法做到简单 ...
- AsyncTask源码解读
AsyncTask源码解读 一.基本使用 protected void onPreExecute() protected abstract Result doInBackground(Params.. ...
- [xDebug] PhpStorm Xdebug远程调试环境搭建
对于简单的工程,直接print_r();exit()已经足够,但是对于大型项目有时就有点力不从心.. 1,环境介绍 本地:windows10(192.168..)+ phpstorm8远程:Cento ...
- 多线程实现tcp聊天服务器
多线程tcp server & client tcp服务端(多线程): from socket import * from threading import Thread def clien ...
- Shell编译安装nginx
环境及规划 [root@nginx-node01 ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) ID 主机名 ip ...
- TensorFlow从0到1之TensorFlow csv文件读取数据(14)
大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 TensorFlow 中读取文件,本节将重点介绍如何从 CSV ...
- (九)HttpClient获取cookies
原文链接:https://blog.csdn.net/cheny1p1ng/article/details/90780024 旧版本DefaultHttpClient 使用getCookieStore ...