题目背景
此题为汕头某中学dalao提供
由其学校内部赛原题改编,并非原题且并未公开
原出题人@月见之兔
曾经幻想过未来的风景
或许有着另外一片天
小镇的远方
有着深远的回忆
也有着富有深情的诗篇
题目描述
Chino非常注重自己的成绩
Chino有 $m$ 种方式给自己增加 $rp$ 以增加成绩,她的每种增加 $rp$ 的方式都有 $n$ 个阶段,第 $i$ 种的第 $j$ 个阶段增加的 $rp$ 表示为 $A_{ij}$ ,表示连续进行了 $j$ 天第 $i$ 种增加 $rp$ 的方式
Chino连续进行同一种方式,效果可能更好也可能更差,她想要知道在 $n$ 天里能获得的最大 $rp$ ,你能帮帮可爱的Chino吗?
输入输出格式
输入格式:第一行,两个正整数 $n$ , $m$
接下来 $m$ 行,第 $i+1$ 行为 $n$ 个整数( $A_{i1} - A_{in}$ )
输出格式:一行一个数,最大的 $rp$
输入输出样例
说明
本题分为3个Subtask
第一个Subtask,2组数据 ,保证 $n \leq 50$ , $m \leq 5000$ , $a_i \leq 1e9$
对于第二个Subtask,4组数据,保证 $n \leq 70$ , $m \leq 10000$ , $a_i \leq 1e9$
对于第三个Subtask,4组数据,保证 $n \leq 100$ , $m \leq 5000$ , $a_i \leq 1e9$
其中每组数据4分,对于每个Subtask及其中的每个数据点,取分数和。
样例解释1
第 $1$ 天进行第 $1$ 项活动,获得 $A_{11}=3$ 点rp。
第 $2$ 天进行第 $2$ 项活动,获得 $A_{21}=3$ 点rp。
第 $3$ 天进行第 $1$ 项活动,获得 $A_{11}=3$ 点rp。
样例解释2
第 $1$ 天进行第 $2$ 项活动,获得 $A_{21}=3$ 点rp。
第 $2$ 天进行第 $2$ 项活动,获得 $A_{22}=5$ 点rp(因为已经连续进行了 $2$ 次第 $2$ 项活动,因而是 $A_{22}$ 而不是 $A_{21}$ 。
第 $3$ 天进行第 $3$ 项活动,获得 $A_{31}=4$ 点rp。
看到题目基本上可以确定这题是道dp题,但是怎么dp我没有想出来
一开始当背包做的,想出来
O
(
n
2
m
2
)
O(n^2m^2)
O(n2m2)的做法,肯定过不了就没打。以为直接背包可以骗一点分,但是并没有。。。
心态就很爆炸,赛后看了题解才会做这道题
我的dp还是弱啊。。。
先是读进来的时候顺便做前缀和,方便一会dp
然后怎么做dp
状态的参数有两个,用
f
i
,
j
f_{i,j}
fi,j表示第i天做的是第j个方案的最大收益
那么
f
i
,
j
=
max
(
f
i
−
t
,
k
+
a
j
,
t
)
f_{i,j}=\max(f_{i-t,k}+a_{j,t})
fi,j=max(fi−t,k+aj,t),
k
!
=
j
,
t
<
=
i
k!=j,t<=i
k!=j,t<=i
那就可以枚举
i
,
j
,
t
,
k
i,j,t,k
i,j,t,k,但是这样的复杂度是
O
(
n
2
m
2
)
O(n^2m^2)
O(n2m2)的
但是注意到,我们只会用到
f
i
−
t
,
k
f_{i-t,k}
fi−t,k中的最大值或次大值
那么我们只需要在递推的时候记录一下即可,无需枚举k,也就是说这里可以贪心
t是要枚举的,这里并不能贪心,因为取
f
i
−
t
,
k
f_{i-t,k}
fi−t,k的最大值和取
a
j
,
t
a_{j,t}
aj,t的最大值都不能保证最优,两者的最大值又不一定能同时取到
这样的复杂度就是
O
(
n
2
m
)
O(n^2m)
O(n2m)的了,这题可以过
那么我们就记录对于每一个
i
,
i
∈
[
1
,
n
]
i,i∈[1,n]
i,i∈[1,n]的最大值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;
}