一道不是十分水的\(dp\).

首先我们考虑\(dp\)方程的构造。起初我定义的状态是\(dp_{i,j}\)表示前\(i\)个格子,总共跳了\(j\)次的最大得分。但事实上它并不可以转移,因为我们不知道新的一轮操作从之间的哪个格子算起。

那么状态转移方程就出来了,我们把第一维改成本次跳到第\(i\)个格子上,包括本次在内总共跳了\(j\)次的最大得分,那么转移的时候,由于本次一定要跳到\(i\)上(如状态中所定义),所以不用分类讨论。方程就是:$$dp_{i,j}=\max{dp_{k,j-1}-\rm{Sum(k + 1, i-1)}}+A_i$$

其中\(0 \leq k < i(\text{不能两次跳到同一个格子上所以右区间为开区间})\),\(\rm{Sum(l,r)}\mathcal{=\sum\limits_{i=l}^{r}A_i}\)

代码大概是这样\((\rm{30pts})\):

#include <cstdio>
#include <iostream> #define MAXN 5010 using namespace std ; int i, j, k, Ans ;
int N, T, S[MAXN], dp[MAXN][MAXN], A[MAXN], B[MAXN] ; int main(){
cin >> N >> T ;
for (i = 1 ; i <= N ; ++ i)
scanf("%d", &A[i]), S[i] = S[i - 1] + A[i] ;
for (i = 1 ; i <= N ; ++ i) scanf("%d", &B[i]) ;
for (i = 0 ; i <= N ; ++ i)
for (j = 0 ; j <= N ; ++ j)
dp[i][j] = -192608170 ; dp[0][0] = 0 ;
for (i = 1 ; i <= N ; ++ i)
for (j = 1 ; j <= i ; ++ j){
for (k = 0 ; k < i ; ++ k)
dp[i][j] = max(dp[i][j], dp[k][j - 1] - S[i - 1] + S[k] + A[i]) ;
if (j % T == 0) dp[i][j] += B[i] ; Ans = max(Ans, dp[i][j]) ;
}
cout << Ans << endl ; return 0 ;
}

但是我们发现,这个复杂度是\(\Theta(n^3)\)的,于是选择优化。\(dp\)优化的老套路就是:

  • 优化状态维数

  • 优化转移复杂度

而此处我们不可以优化状态了,所以考虑优化转移复杂度。转移的复杂度是\(\Theta(n)\)的,我们考虑可否\(\Theta(1)\)转移,最终使得总复杂度为\(\Theta(n^2) \times \Theta(1) \leq O(n^2)\)

从状态转移方程入手,我们发现有关于\(k\)是满足单调性的。所以不妨我们记录一下每次的\(k\),即把\(dp[k][j-1]+ S[k]\)中的最大值存储下来,从而达到\(\Theta(1)\)转移的目的。

此处笔者使用了比较玄学的存储方式……类似刷表……当然这个地方有多种的优化方式啦~

完整版\(code\)(700~800ms):

#include <cstdio>
#include <iostream> #define MAXN 5010 using namespace std ; int i, j, k, p, Ans ;
int N, T, Last[MAXN], S[MAXN], dp[MAXN][MAXN], A[MAXN], B[MAXN] ; int main(){
cin >> N >> T ;
for (i = 1 ; i <= N ; ++ i)
scanf("%d", &A[i]), S[i] = S[i - 1] + A[i] ;
for (i = 1 ; i <= N ; ++ i) scanf("%d", &B[i]) ;
for (i = 0 ; i <= N ; ++ i)
for (j = 0 ; j <= N ; ++ j)
dp[i][j] = -192608170 ; dp[0][0] = 0 ;
for (j = 1 ; j <= N ; ++ j){
for (i = j ; i <= N ; ++ i){
p = Last[i - j], Last[i - j] = 0 ;
dp[i][j] = p - S[i - 1] + A[i] ;
if (j % T == 0) dp[i][j] += B[i] ; Ans = max(Ans, dp[i][j]) ;
Last[i - j] = max(Last[i - j - 1], dp[i][j] + S[i]) ;
}
}
cout << Ans << endl ; return 0 ;
}

毒瘤常数优化后被艹到龟速的版本(1100ms +):

#include <cstdio>
#include <cstring>
#include <iostream> #define max Max
#define MAXN 5010
#define Inf 19260817 using namespace std ; int i, j, k, p, t, Ans ;
int N, T, Last[MAXN], S[MAXN], dp[MAXN][MAXN], A[MAXN], B[MAXN] ; inline int Max(int a, int b){
return a & ((b - a) >> 31) | b & ( ~ (b - a) >> 31) ;
}
inline int qr(){
int res = 0 ; char c = getchar() ;
while (!isdigit(c)) c = getchar() ;
while (isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar() ;
return res ;
}
int main(){
cin >> N >> T ;
for (i = 0 ; i <= N ; ++ i)
for (j = 0 ; j <= N ; ++ j)
dp[i][j] = -Inf ; dp[0][0] = 0 ;
for (i = 1 ; i <= N ; ++ i)
A[i] = qr(), S[i] = S[i - 1] + A[i] ;
for (i = 1 ; i <= N ; ++ i) B[i] = qr() ;
for (j = 1 ; j <= N ; ++ j){
for (i = j ; i <= N ; ++ i){
t = i - j, p = Last[t], dp[i][j] = p - S[i - 1] + A[i] ;
if (!(j % T)) dp[i][j] += B[i] ; Ans = max(Ans, dp[i][j]), Last[t] = max(Last[t - 1], dp[i][j] + S[i]) ;
}
}
cout << Ans << endl ; return 0 ;
}

唉,先有常数后有天,反向优化\(Sun\)神仙啊

随机推荐

  1. jQuery通用的全局遍历方法$.each()用法实例

    1.jQuery通用的全局遍历方法$.each()用法 2. test.json文件代码: 3. html代码 4.jQuery代码 <script src="jquery-1.3.1 ...

  2. Software-Defined Networking之搬砖的故事

    在很久很久以前,有一个村子. 村里的每一户,都有一个男人和一个女人. 每一户,都以搬砖为生. 从不同的地方,搬到不同的地方. 男人负责搬砖,女人负责告诉男人往哪搬. 每个家庭,都服从村委会的指挥. 村 ...

  3. 背景平铺(兼容IE8)

    标准浏览器通过background-size属性设置;IE8以下通过滤镜实现. 代码如下: /* IE8 */ filter: progid:DXImageTransform.Microsoft.Al ...

  4. python-命令模式

    源码地址:https://github.com/weilanhanf/PythonDesignPatterns 说明: 命令在发送方被激活,而在接收方被响应.一个对象既可以作为命令的发送方,也可以作为 ...

  5. IO流作业

    IO流作业 一.    填空题 Java IO流可以分为    字节流          和处理流两大类,其中前者处于IO操作的第一线,所有操作必须通过他们进行. 输入流的唯一目的是提供通往数据的通道 ...

  6. BZOJ2957: 楼房重建(分块)

    题意 题目链接 Sol 自己YY出了一个\(n \sqrt{n} \log n\)的辣鸡做法没想到还能过.. 可以直接对序列分块,我们记第\(i\)个位置的值为\(a[i] = \frac{H_i}{ ...

  7. Android逆向 APK文件组成

    一 了解APK文件 我们知道Android系统能运行的程序是.apk文件格式,其实它就是一个压缩包而已,把.apk修改成.zip,然后解压就可以得到该apk内部的文件结构. PS: 既然可以把apk文 ...

  8. linux下opencv编译

    .tar.gz cd opencv-/ cd .. mkdir my_build_dir cd my_build_dir cmake ../opencv- -DWITH_GTK_2_X=ON -DCM ...

  9. SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析 (转载)

    在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...

  10. python给邮箱发送消息

    首先要用到两个模块  并且大同你的发送邮箱smtp 最开始测试没打通了好久 smtplib是提供邮箱smtp服务, email是提供你发送消息的格式之类服务 import smtplib from e ...