Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)

easy 题目链接 & hard 题目链接

给出一张 \(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;
}
posted @ 2020-04-24 17:28  tzc_wk  阅读(128)  评论(2编辑  收藏  举报