zoukankan      html  css  js  c++  java
  • Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)

    easy 题目链接 & hard 题目链接

    给出一张 (n imes 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 imes 10^4)(1 leq k leq 20)
    hard 数据范围:(1 leq n leq 50)(1 leq m leq 2 imes 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}+sumlimits_{t=l}^{min(l+k-1,m)}a_{i,t}+sumlimits_{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}+sumlimits_{t=l}^{min(j+k-1,m)}a_{i,t} (j-k le l le j))
    • (dp_{i,j}=dp_{i-1,l}+sumlimits_{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}+sumlimits_{t=l}^{min(l+k-1,m)}a_{i,t}+sumlimits_{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('
    ')
    #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('
    ')
    #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;
    }
    
  • 相关阅读:
    一个很实用的css3兼容工具很多属性可以兼容到IE6
    html5 canvas 填充渐变形状
    找到任何形状的中心-总结篇
    html canvas非正方旋转和缩放...写的大多是正方的有人表示一直看正方的看厌了
    把jQuery的类、插件封装成seajs的模块的方法
    那些年实用但被我忘掉javascript属性.onresize
    总有一些实用javascript的元素被人遗忘在角落-slice
    jquery(入门篇)无缝滚动
    html5 canvas旋转+缩放
    今天看到这篇新闻之后,决定休息一下咯
  • 原文地址:https://www.cnblogs.com/ET2006/p/12768893.html
Copyright © 2011-2022 走看看