题目背景
此题为汕头某中学dalao提供
由其学校内部赛原题改编,并非原题且并未公开
原出题人@月见之兔
曾经幻想过未来的风景
或许有着另外一片天
小镇的远方
有着深远的回忆
也有着富有深情的诗篇
题目描述
Chino非常注重自己的成绩
Chino有 种方式给自己增加 以增加成绩,她的每种增加 的方式都有 个阶段,第 种的第 个阶段增加的 表示为 ,表示连续进行了 天第 种增加 的方式
Chino连续进行同一种方式,效果可能更好也可能更差,她想要知道在 天里能获得的最大 ,你能帮帮可爱的Chino吗?
输入输出格式
输入格式:第一行,两个正整数 ,
接下来 行,第 行为 个整数( )
输出格式:一行一个数,最大的
输入输出样例
说明
本题分为3个Subtask
第一个Subtask,2组数据 ,保证 , ,
对于第二个Subtask,4组数据,保证 , ,
对于第三个Subtask,4组数据,保证 , ,
其中每组数据4分,对于每个Subtask及其中的每个数据点,取分数和。
样例解释1
第 天进行第 项活动,获得 点rp。
第 天进行第 项活动,获得 点rp。
第 天进行第 项活动,获得 点rp。
样例解释2
第 天进行第 项活动,获得 点rp。
第 天进行第 项活动,获得 点rp(因为已经连续进行了 次第 项活动,因而是 而不是 。
第 天进行第 项活动,获得 点rp。
看到题目基本上可以确定这题是道dp题,但是怎么dp我没有想出来
一开始当背包做的,想出来
的做法,肯定过不了就没打。以为直接背包可以骗一点分,但是并没有。。。
心态就很爆炸,赛后看了题解才会做这道题
我的dp还是弱啊。。。
先是读进来的时候顺便做前缀和,方便一会dp
然后怎么做dp
状态的参数有两个,用
表示第i天做的是第j个方案的最大收益
那么
,
那就可以枚举
,但是这样的复杂度是
的
但是注意到,我们只会用到
中的最大值或次大值
那么我们只需要在递推的时候记录一下即可,无需枚举k,也就是说这里可以贪心
t是要枚举的,这里并不能贪心,因为取
的最大值和取
的最大值都不能保证最优,两者的最大值又不一定能同时取到
这样的复杂度就是
的了,这题可以过
那么我们就记录对于每一个
的最大值b[i]和次大值sb[i]以及它取到最大值时的方案编号bi[i]
然后就还有一些代码细节问题了,比如题解的代码的每个i的最大和次大的处理就比较厉害,如果当前值>扫过的当前的最大值,交换当前值与扫过的当前的最大值,然后再把当前值与次大值比较,可能说比较不太清晰,看代码就会比较清晰
–
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define For(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
inline int read()
{
char c;
bool t=0;
int a=0;
while((c=getchar())==' '||c=='\n'||c=='\r');
if(c=='-')
{
t=1;
c=getchar();
}
while(isdigit(c))
{
a*=10;
a+=(c-'0');
c=getchar();
}
return a*(t?-1:1);
}
int n,m,a[10010][101],b[110],bi[110],sb[110];
inline int get(int i,int j)
{
return bi[i]==j?sb[i]:b[i];
}
int main()
{
int temp;
n=read();m=read();
For(i,1,m)
For(j,1,n)
{
a[i][j]=read();
a[i][j]+=a[i][j-1];
}
For(i,1,n)
For(j,1,m)
{
temp=0;
For(k,1,i)
temp=max(temp,get(i-k,j)+a[j][k]);
if(temp>b[i])
{
swap(temp,b[i]);
bi[i]=j;
}
if(temp>sb[i])
sb[i]=temp;
}
printf("%d",b[n]);
return 0;
}