题目描述

给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分。

输入输出格式

输入格式:

第一行,一个正整数T,表示有T组数据。(T<=100)

接着T行,每行第一个数为n,接着n个整数表示给定的序列.

输出格式:

输出T行,每行一个整数,表示A的得分

输入输出样例

输入样例#1:
复制

2
1 -1
2 1 2
输出样例#1: 复制

-1
3

说明

时限:3s


题解

首先让我们试试暴力思路:

设A的得分为VA,B的得分为VB

那么在(VA-VB)取得最大值时,有VA最大。

证明:VA-VB=VA-(Sum-VA)=2*VA-Sum

设F[L][R]为当前还剩[L][R]时,(先手得分-后手得分)的最大值。


若当前正在处理F[L][R],那么存在三种选择方案:

 1.全取。

  F[L][R]=∑{ v[i]  |  L < = i < = R }

 2.从左边取一些。(保留L'到R)

  F[L][R]=∑{ v[i] | L <= i <= L'-1 }-F[L'][R]

 3.从右边取一些。(保留L到R')

  F[L][R]=∑{ v[i] | R'+1 <= i <= R }-F[L][R']

   关于减去F[L'][R]:

   当区间转换到[L',R]时的F值,为转换后的先手-后手,也就是当前意义下的后手-先手,

   所以-(后手-先手)=先手-后手。

这样,我们从小到大枚举区间,每次在这三种决策中取一种最优决策,

最后的max(VA-VB)=F[1][n],

   max(VA)=(F[1][n]+Sum)/2。

于是我们可以很较为轻松的写出O(n^3)的暴力啦!

 /*
qwerta
P1430 序列取数
Unaccepted
40
代码 C++,0.94KB
提交时间 2018-09-19 18:57:13
耗时/内存
19351ms, 4628KB
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define R register
inline int read()
{
char ch=getchar();
int x=;bool s=;
while(!isdigit(ch)){if(ch=='-')s=;ch=getchar();}
while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
return s?x:-x;
}
int s[];
int f[][];
//int m[1007][1007];
int main()
{
//freopen("a.in","r",stdin);
int t=read();
while(t--)
{
int n=read();
for(R int i=;i<=n;++i)
s[i]=s[i-]+read();
for(R int len=;len<=n;++len)
for(R int l=;l+len-<=n;++l)
{ int r=l+len-;
f[l][r]=s[r]-s[l-];
for(R int _l=l+;_l<=r;++_l)
f[l][r]=max(f[l][r],s[_l-]-s[l-]-f[_l][r]);
for(R int _r=r-;_r>=l;--_r)
f[l][r]=max(f[l][r],s[r]-s[_r]-f[l][_r]); }
printf("%d\n",(f[][n]+s[n])>>);
}
return ;
}

但是想要通过1e3的数据,O(n^3*t)的时间复杂度肯定是布星的。

所以我们还需要一点儿优化。

考虑状态数是n^2的,雷打不动,所以我们要将贼手伸向状态转移。

以从右取为例:

F[L][R]=max(S[R]-S[R']-F[L][R']) //(L <= R' <= R-1)

 =max(S[R]-(S[R']+F[L][R']))

如果我们能知道R'在L到R-1上时,S[R']+F[L][R']的最小值,那么就能O(1)转移了。

Min[L][R]=min{S[R']+F[L][R'] | L <= R' <= R }

那么有

Min[L][R-1]=min{S[R']+F[L][R'] | L <= R' <= R-1 }

也就是说

Min[L][R]=min(Min[L][R-1],S[R]+F[L][R])

这样,在循环枚举区间的同时顺便维护一下Min[L][R],就可以实现O(1)转移了。

其实就是把暴力中间那两句话换了种说法而已。

 /*
qwerta
P1430 序列取数
Accepted
100
代码 C++,1.63KB
提交时间 2018-09-19 20:05:27
耗时/内存
4910ms, 12444KB
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define R register
inline int read()
{
char ch=getchar();
int x=;bool s=;
while(!isdigit(ch)){if(ch=='-')s=;ch=getchar();}
while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
return s?x:-x;
}//快读(这题略卡常)
int s[];
int f[][];
int ml[][];
int mr[][];
//设ml为固定L端时的min值,mr为固定R端时的max值
void write(int x)
{
if(x>)write(x/);
putchar(x%+'');
return;
}//快写
int main()
{
//freopen("a.in","r",stdin);
int t=read();
while(t--)
{
int n=read();
for(R int i=;i<=n;++i)
s[i]=s[i-]+read();//前缀和
for(R int l=;l<=n;++l)
{
f[l][l]=s[l]-s[l-];
ml[l][l]=s[l]+f[l][l];
mr[l][l]=s[l-]-f[l][l];
}//预处理
for(R int len=;len<=n;++len)
for(R int l=,r=len;r<=n;++l,++r)//枚举区间
{
f[l][r]=s[r]-s[l-];
//mr
//for(R int _l=l+1;_l<=r;++_l)
//f[l][r]=max(f[l][r],s[_l-1]-s[l-1]-f[_l][r]);
f[l][r]=max(f[l][r],mr[l+][r]-s[l-]);
//ml
//for(R int _r=r-1;_r>=l;--_r)
//f[l][r]=max(f[l][r],s[r]-s[_r]-f[l][_r]);
f[l][r]=max(f[l][r],s[r]-ml[l][r-]);
//
ml[l][r]=min(ml[l][r-],s[r]+f[l][r]);
mr[l][r]=max(mr[l+][r],s[l-]-f[l][r]);
}
int x=(f[][n]+s[n])>>;
if(x<){putchar('-');write(-x);}
else write(x);
putchar('\n');
//输出答案
}
return ;
}

「LuoguP1430」 序列取数(区间dp的更多相关文章

  1. 「LuoguP1220」 关路灯(区间dp

    题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯. 为了给村 ...

  2. 「网络流24题」「LuoguP2774」方格取数问题(最大流 最小割

    Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方 ...

  3. 【Luogu】P1430序列取数(DP)

    题目链接 博弈DP太喵了qwq 设f[i][j]表示剩下区间[i,j]要取,先手最大值 明显我们要从这区间里面拿个最大的 就等价于这段区间的前缀和,我们要给对手留下个最小的 就是f[i][j]=sum ...

  4. LibreOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流

    #6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  5. Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)

    Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流) Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从 ...

  6. 洛谷 P1430 序列取数 解题报告

    P1430 序列取数 题目描述 给定一个长为\(n\)的整数序列\((n<=1000)\),由\(A\)和\(B\)轮流取数(\(A\)先取).每个人可从序列的左端或右端取若干个数(至少一个), ...

  7. loj #2051. 「HNOI2016」序列

    #2051. 「HNOI2016」序列 题目描述 给定长度为 n nn 的序列:a1,a2,⋯,an a_1, a_2, \cdots , a_na​1​​,a​2​​,⋯,a​n​​,记为 a[1: ...

  8. 「HNOI2016」序列 解题报告

    「HNOI2016」序列 有一些高妙的做法,懒得看 考虑莫队,考虑莫队咋移动区间 然后你在区间内部找一个最小值的位置,假设现在从右边加 最小值左边区间显然可以\(O(1)\),最小值右边的区间是断掉的 ...

  9. 「SDOI2016」储能表(数位dp)

    「SDOI2016」储能表(数位dp) 神仙数位 \(dp\) 系列 可能我做题做得少 \(QAQ\) \(f[i][0/1][0/1][0/1]\) 表示第 \(i\) 位 \(n\) 是否到达上界 ...

随机推荐

  1. 线程安全-一个VC下多个网络请求

    一.线程安全变量控制显示隐藏loading框 问题描写叙述: 同一页面有两个异步网络请求,第一个请求開始,loading旋转.第二个请求開始loading旋转.第一个结束,loading停止旋转,但是 ...

  2. 如何直接打开使用locate等查找到的文件

    很多的时候需要使用locate去定位文件,找到了文件之后接下来就是使用相应的文本编辑工具如gvim进行打开. 这个时候最烦心的就是去复制一大长串的地址了. 如果能让定位和打开一体操作就最好了,这就需要 ...

  3. win10任务管理器开机老是自己打开

    win10任务管理器开机老是自己打开 学习了:https://zhidao.baidu.com/question/332868722086816045.html 还没有注意过这个东西:系统失败-> ...

  4. JMeter 压力測试使用函数和 CSV 文件參数化 json 数据

            在 http Load Testing 中.json 数据的提交是个让人头疼的问题.本文具体介绍怎样进行 JMeter 的 json 測试提交,以及怎样将其參数化.        St ...

  5. SpringBoot学习之@Controller和@RestController

    今天我们来研究一下@Controller和@RestController的用法 @Controller 1.Controller可以用来跳转页面,并且必须配合模板来使用. @Controller // ...

  6. java 堆和栈一般理解

    栈与堆都是Java用来在Ram中存放数据的地方.与C++不同.Java自己主动管理栈和堆.程序猿不能直接地设置栈或堆.  Java的堆是一个执行时数据区,类的(对象从中分配空间.这些对象通过new.n ...

  7. C# 之 集合ArrayList

    .NET Framework提供了用于数据存储和检索的专用类,这些类统称集合. 这些类提供对堆栈.队列.列表和哈希表的支持.大多数集合类实现系统的接口.以下我们主要来讲一下ArrayList.     ...

  8. IP分配及网段划分

    1.IP我们先来了解一下3类常用的IP A类IP段 0.0.0.0 到127.255.255.255  B类IP段 128.0.0.0 到191.255.255.255  C类IP段 192.0.0. ...

  9. iOS 7 中 StoryBoard 总体缩放

    iOS 7 中 StoryBoard 总体缩放 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用 ...

  10. 多项式相乘快速算法原理及相应C代码实现---用到fft

    最近认真研究了一下算法导论里面的多项式乘法的快速计算问题,主要是用到了FFT,自己也实现了一下,总结如下. 1.多项式乘法 两个多项式相乘即为多项式乘法,例如:3*x^7+4*x^5+1*x^2+5与 ...