Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)
给出一张 \(n \times m\) 的矩阵,每个格子上面有一个数,你要在每行选出一个点 \((i,t)\),并覆盖左上角为 \((i,t)\),右下角为 \((\min(i+1,n),\min(t+k-1,m))\) 的矩形。求被覆盖的格子上数的和的最大值。
easy 数据范围:\(1 \leq n \leq 50\),\(1 \leq m \leq 2 \times 10^4\),\(1 \leq k \leq 20\)。
hard 数据范围:\(1 \leq n \leq 50\),\(1 \leq m \leq 2 \times 10^4\),\(1 \leq k \leq m\)。
首先可以想到 \(dp\)。发现第 \(i\) 行对答案的贡献只与第 \(i\) 行与第 \(i-1\) 行的贡献有关,因此设 \(dp_{i,j}\) 为扫到第 \(i\) 行,第 \(i\) 行选择的数是 \(j\),前 \(i\) 行被覆盖的数的最大和。
很显然可以枚举上一行填的数 \(l\)。那么可以得到状态方程:
- \(dp_{i,j}=dp_{i-1,l}+\sum\limits_{t=l}^{\min(l+k-1,m)}a_{i,t}+\sum\limits_{t=l}^{\min(j+k-1,m)}a_{i,t}\ \ \ (l \leq j-k\ \ \operatorname{or}\ \ l \geq j+k)\)
- \(dp_{i,j}=dp_{i-1,l}+\sum\limits_{t=l}^{\min(j+k-1,m)}a_{i,t}\ \ \ (j-k \le l \le j)\)
- \(dp_{i,j}=dp_{i-1,l}+\sum\limits_{t=j}^{\min(l+k-1,m)}a_{i,t}\ \ \ (j \leq l \le j+k)\)
里面的 \(\sum\) 可以预处理单行的前缀和 \(s_{i,j}\),\(\mathcal O(1)\) 求出。
这个算法是 \(\mathcal O(nm^2)\) 的,朴素转移会 TLE。
不过发现第一种情况可以记录前缀后缀 \(dp_{i-1,j}+\sum\limits_{t=l}^{\min(l+k-1,m)}a_{i,t}+\sum\limits_{t=l}^{\min(j+k-1,m)}a_{i,t}\) 中的最大值,第一种情况就可以 \(\mathcal O(1)\) 转移了。F1 就被我们搞掉了。
对于 F2,很显然要 \(dp\) 优化,看到这种决策变量在一个区间中的,可以想到线段树/单调队列优化。这里讲单调队列优化。
以第二种情况为例,将原来的式子转化为:\(dp_{i-1,l}+s_{i,\min(j+k-1,m)}-s_{i,l-1}\)。
拆成两部分,一部分与 \(l\) 有关,一部分与 \(j\) 有关,即 \((s_{i,\min(j+k-1,m)})+(dp_{i-1,j}-s_{i,l-1})\)。
用单调队列维护 \((dp_{i-1,j}-s_{i,l-1})\) 的最大值,可以做到均摊 \(\mathcal O(1)\) 的时间复杂度。
第三种情况也类似,原式可化为 \(dp_{i-1,l}+s_{i,min(l+k-1,m)}-s_{i,j-1}\)
还是拆成两部分,\(-s_{i,j-1}+(dp_{i-1,l}+s_{i,min(l+k-1,m))}\)
单调队列维护 \((dp_{i-1,l}+s_{i,min(l+k-1,m)})\) 的最大值。注意此处要倒序枚举。
最后求 \(\max dp_{n,i}\) 就可以了。
F1:
//Coded by tzc_wk
/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define giveup(...) return printf(__VA_ARGS__),0;
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define mask(a) (1ll<<(a))
#define maskx(a,x) ((a)<<(x))
#define _bit(a,x) (((a)>>(x))&1)
#define _sz(a) ((int)(a).size())
#define filei(a) freopen(a,"r",stdin);
#define fileo(a) freopen(a,"w",stdout);
#define fileio(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
#define eprintf(...) fprintf(stderr,__VA_ARGS__)
#define put(x) putchar(x)
#define eoln put('\n')
#define space put(' ')
#define y1 y_chenxiaoyan_1
#define y0 y_chenxiaoyan_0
#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
inline void print(int x){
if(x<0){
putchar('-');
print(abs(x));
return;
}
if(x<=9) putchar(x+'0');
else{
print(x/10);
putchar(x%10+'0');
}
}
inline int qpow(int x,int e,int _MOD){
int ans=1;
while(e){
if(e&1) ans=ans*x%_MOD;
x=x*x%_MOD;
e>>=1;
}
return ans;
}
int n=read(),m=read(),k=read(),a[55][20005],sum[55][20005],dp[55][20005],mxp[55][20005],mxs[55][20005];
signed main(){
fz(i,1,n) fz(j,1,m) a[i][j]=read(),sum[i][j]=sum[i][j-1]+a[i][j];
fz(i,1,m) dp[1][i]=sum[1][min(i+k-1,m)]-sum[1][i-1];
fz(i,1,m) mxp[1][i]=max(mxp[1][i-1],dp[1][i]+sum[2][min(i+k-1,m)]-sum[2][i-1]);
fd(i,m,1) mxs[1][i]=max(mxs[1][i+1],dp[1][i]+sum[2][min(i+k-1,m)]-sum[2][i-1]);
fz(i,2,n){
fz(j,1,m){
if(j+k<=m) dp[i][j]=max(dp[i][j],mxs[i-1][j+k]+sum[i][j+k-1]-sum[i][j-1]);
if(j-k>=1) dp[i][j]=max(dp[i][j],mxp[i-1][j-k]+sum[i][j+k-1]-sum[i][j-1]);
fz(l,max(j-k+1,1ll),min(j+k-1,m)){
dp[i][j]=max(dp[i][j],dp[i-1][l]+sum[i][min(max(j+k-1,l+k-1),m)]-sum[i][min(j,l)-1]);
}
// cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
}
fz(j,1,m) mxp[i][j]=max(mxp[i][j-1],dp[i][j]+sum[i+1][min(j+k-1,m)]-sum[i+1][j-1]);
fd(j,m,1) mxs[i][j]=max(mxs[i][j+1],dp[i][j]+sum[i+1][min(j+k-1,m)]-sum[i+1][j-1]);
}
int ans=0;
fz(i,1,m) ans=max(ans,dp[n][i]);
cout<<ans<<endl;
return 0;
}
F2:
//Coded by tzc_wk
/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define giveup(...) return printf(__VA_ARGS__),0;
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define mask(a) (1ll<<(a))
#define maskx(a,x) ((a)<<(x))
#define _bit(a,x) (((a)>>(x))&1)
#define _sz(a) ((int)(a).size())
#define filei(a) freopen(a,"r",stdin);
#define fileo(a) freopen(a,"w",stdout);
#define fileio(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
#define eprintf(...) fprintf(stderr,__VA_ARGS__)
#define put(x) putchar(x)
#define eoln put('\n')
#define space put(' ')
#define y1 y_chenxiaoyan_1
#define y0 y_chenxiaoyan_0
#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
inline void print(int x){
if(x<0){
putchar('-');
print(abs(x));
return;
}
if(x<=9) putchar(x+'0');
else{
print(x/10);
putchar(x%10+'0');
}
}
inline int qpow(int x,int e,int _MOD){
int ans=1;
while(e){
if(e&1) ans=ans*x%_MOD;
x=x*x%_MOD;
e>>=1;
}
return ans;
}
int n=read(),m=read(),k=read(),a[55][20005],sum[55][20005],dp[55][20005],mxp[55][20005],mxs[55][20005];
int rit(int x){
return ((x+k-1)>m)?(m):(x+k-1);
}
signed main(){
fz(i,1,n) fz(j,1,m) a[i][j]=read(),sum[i][j]=sum[i][j-1]+a[i][j];
fz(i,1,m) dp[1][i]=sum[1][min(i+k-1,m)]-sum[1][i-1];
fz(i,1,m) mxp[1][i]=max(mxp[1][i-1],dp[1][i]+sum[2][min(i+k-1,m)]-sum[2][i-1]);
fd(i,m,1) mxs[1][i]=max(mxs[1][i+1],dp[1][i]+sum[2][min(i+k-1,m)]-sum[2][i-1]);
fz(i,2,n){
deque<pii> q1,q2;
fz(j,1,m){
if(j+k<=m) dp[i][j]=max(dp[i][j],mxs[i-1][j+k]+sum[i][rit(j)]-sum[i][j-1]);
if(j-k>=1) dp[i][j]=max(dp[i][j],mxp[i-1][j-k]+sum[i][rit(j)]-sum[i][j-1]);
// fz(l,max(j-k+1,1ll),min(j+k-1,m)){
// dp[i][j]=max(dp[i][j],dp[i-1][l]+sum[i][min(max(j+k-1,l+k-1),m)]-sum[i][min(j,l)-1]);
// }
// cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
}
fz(j,1,m){
while(!q1.empty()&&q1.front().se<=j-k) q1.pop_front();
if(!q1.empty()) dp[i][j]=max(dp[i][j],q1.front().fi+sum[i][rit(j)]);
while(!q1.empty()&&dp[i-1][j]-sum[i][j-1]>q1.back().fi) q1.pop_back();
q1.push_back({dp[i-1][j]-sum[i][j-1],j});
}
fd(j,m,1){
while(!q2.empty()&&q2.front().se>=j+k) q2.pop_front();
while(!q2.empty()&&dp[i-1][j]+sum[i][rit(j)]>q2.back().fi) q2.pop_back();
q2.push_back({dp[i-1][j]+sum[i][rit(j)],j});
dp[i][j]=max(dp[i][j],q2.front().fi-sum[i][j-1]);
}
// fz(j,1,m){
// cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
// }
fz(j,1,m) mxp[i][j]=max(mxp[i][j-1],dp[i][j]+sum[i+1][min(j+k-1,m)]-sum[i+1][j-1]);
fd(j,m,1) mxs[i][j]=max(mxs[i][j+1],dp[i][j]+sum[i+1][min(j+k-1,m)]-sum[i+1][j-1]);
}
int ans=0;
fz(i,1,m) ans=max(ans,dp[n][i]);
cout<<ans<<endl;
return 0;
}
Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)的更多相关文章
- 【简洁易懂】CF372C Watching Fireworks is Fun dp + 单调队列优化 dp优化 ACM codeforces
题目大意 一条街道有$n$个区域. 从左到右编号为$1$到$n$. 相邻区域之间的距离为$1$. 在节日期间,有$m$次烟花要燃放. 第$i$次烟花燃放区域为$a_i$ ,幸福属性为$b_i$,时间为 ...
- 单调队列优化DP || [NOI2005]瑰丽华尔兹 || BZOJ 1499 || Luogu P2254
题外话:题目极好,做题体验极差 题面:[NOI2005]瑰丽华尔兹 题解: F[t][i][j]表示第t时刻钢琴位于(i,j)时的最大路程F[t][i][j]=max(F[t-1][i][j],F[t ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- bzoj1855: [Scoi2010]股票交易--单调队列优化DP
单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...
- hdu3401:单调队列优化dp
第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...
- Parade(单调队列优化dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others) ...
- BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP
BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...
- 【单调队列优化dp】 分组
[单调队列优化dp] 分组 >>>>题目 [题目] 给定一行n个非负整数,现在你可以选择其中若干个数,但不能有连续k个数被选择.你的任务是使得选出的数字的和最大 [输入格式] ...
- [小明打联盟][斜率/单调队列 优化dp][背包]
链接:https://ac.nowcoder.com/acm/problem/14553来源:牛客网 题目描述 小明很喜欢打游戏,现在已知一个新英雄即将推出,他同样拥有四个技能,其中三个小技能的释放时 ...
随机推荐
- 前端面试题之手写promise
前端面试题之Promise问题 前言 在我们日常开发中会遇到很多异步的情况,比如涉及到 网络请求(ajax,axios等),定时器这些,对于这些异步操作我们如果需要拿到他们操作后的结果,就需要使用到回 ...
- 【c++ Prime 学习笔记】第2章 变量和基本类型
2.1 基本内置类型 基本数据类型包含了算术类型(arithmetic type)和空类型(void) 算数类型,包含了字符.整型数.布尔值和浮点数 空类型,不对应具体的值 2.1.1 算术类型 算术 ...
- .Net2.0连接PG数据注意事项
.Net2.0连接PG数据注意事项 第一次用.net操作PG[.NET2.0] 一:Npgsql版本问题 1:如果是.net2.0 建议用2.0.11.0[NuGet搜索npgsql第一个的最低版本 ...
- UltraSoft - Beta - 发布声明
1. Beta版本更新内容 新功能 (1)消息中心页面 课程爬取到新DDL.资源时会以通知的方式通知用户,本次同步更新了哪些内容一目了然.此外,当被作为参与成员添加DDL时也会通知.一些系统通知也会放 ...
- 【二食堂】Beta - Scrum Meeting 1
Scrum Meeting 1 例会时间:5.13 18:30~18:50 进度情况 组员 当前进度 今日任务 李健 1. 查阅资料,解决划词勾选和右键菜单的问题issue2. 修复了Alpha阶段的 ...
- oo第二次博客-三次电梯调度的总结与反思
本单元从电梯调度相关问题层层深入,带领我们学习并运用了了多线程相关的知识. 三次电梯调度依次为单电梯单容量.单电梯可携带.多电梯可携带. 一.我的设计 在第一次作业中,使用了最简单的FIFO调度方法. ...
- Nginx(二):Nginx的四层(L4)和七层(L7)负载均衡
OSI七层模型 和 TCP/IP四层模型 四层负载均衡( L4 Load Balancing ) 四层负载均衡,主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内 ...
- 像素反转 牛客网 程序员面试金典 C++ Python
像素反转 牛客网 程序员面试金典 题目描述 有一副由NxN矩阵表示的图像,这里每个像素用一个int表示,请编写一个算法,在不占用额外内存空间的情况下(即不使用缓存矩阵),将图像顺时针旋转90度. 给定 ...
- 滑动窗口的最大值 牛客网 剑指Offer
滑动窗口的最大值 牛客网 剑指Offer 题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6 ...
- shell 脚本控制命令的执行顺序
&&,||,(),{},& 五个符号的运用shell脚本执行命令的时候,有时候会依赖于前一个命令是否执行成功.而&&和||就是用来判断前一个命令执行效果的. 也 ...