• cqyz oj |【综合训练】百层游戏 P2729 | DP动态规划 | 滑动窗口


    Description

    小沐最近迷上一款称为“100层”的游戏,其规则如下:
    1、最开始角色在第一层某个房间。
    2、每一层包含m个房间,在同一层上,角色可以向一个方向走,即要么向左,要么向右,但最多经过连续T个房间后到达同层一个房间(这里你可理解为连续经过T+1个房间)。
    3、在每一层也可直接走到上一层,即从第i层的第j个房间,可以直接上到第i+1层的第j个房间。
    4、角色每经过一个房间和到达一个房间,会获得一定的分数。所以角色最终获得的分数是它经过房间的分数之和。
    5、游戏的目标是角色从第1层走到最高层要获取最高的分数。

    Input

      第一行包含四个整数:n,m,x,T,表示游戏共有n层,每层有m个房间,角色最初在第1层的第x个房间,角色在同一层上向左或向右最多经过T个房间。  接下来的n行,每行包含m个整数,其中第i+1行的第j个整数,表示第i 层第j个房间的分数。

    Output

      一个整数,表示获取的最高分。

    Sample Input 1

    3 3 2 1
    7 8 1
    4 5 6
    1 2 3

    Sample Output 1

    29

    Hint

    1<=N<=100,1<=M<=10000,1<=X,T<=M,-500<=房间分数<=500


    一看到这个题就想起了另一个题目cqyz oj|P1419 HB办证,但是多了一个限制条件——每一层向左向右走的最大房间数量。
    由那个题的思路,定义状态函数(dp(i,j))表示第(i)层第(j)个房间得分的最大值,设(a[i][j])为第(i)层第(j)个房间的得分,可以先得到一个粗略的思路:

    [dp(i,j)=max left{ egin{array}{} max { dp(i-1,k)+sum_{k=j-T}^{j}a[i][k]} \ max { dp(i-1,l)+sum_{k=j}^{j+T}a[i][k]} end{array} ight} ]

    初始化(dp(i,j)=-inf)(dp(0,x)=0)
    其中(k)是从该房间向左和向右走不超过(T)个房间到达的房间号,按照这个方程直接枚举(k)(O(T)),再计算连续和(O(T)),写出程序,时间复杂度(O(n*m*T*T)=O(10^{14}))妥妥的超时

    于是想到要进行优化,很容易想到的一个是对每一层先计算出前缀和和后缀和,将计算连续和的时间减为O(1),转移方程(以下以前缀和为例)变为

    [dp(i,j)=max left{ egin{array}{} max{dp(i-1,k)+sum(j)-sum(k-1)|j-T<=k<=j} \ max{dp(i-1,k)+sum(k)-sum(j-1)|j<=k<=j+T} end{array} ight} ]

    这里的sum(i)即前缀和

    但是时间(O(n*m*T)=O(10^{10}))依然超时,还要继续优化。这时候从方程入手好像没有什么可以优化的地方了,所以就考虑从怎么减少枚举(k)的时间入手
    接下来以向左走为例(开始抄讲稿)

    设:(v1=max{dp(i-1,k)+sum(j)-sum(k-1) | j-T<=k<=j })
    变形为:(v1=max{dp(i-1,k)-sum(k-1) | j-T<=k<=j } + sum(j))
    观察:(max{dp(i-1,k) -sum(k-1) | j-T<=k<=j })实质就是在窗口([j-T,j])中选择 (dp(i-1,k)-sum(k-1))最大的元素。

    以上变形是可以用滑动窗口优化的关键,在窗口中只需要维护(dp(i-1,k)-sum(k-1))的最大值而没有当前房间(j)的值干扰。
    经过优化,每次只要取出队首和(sum(j))相减就可以得到(dp(i,j)),枚举时间降至O(1),由于滑动窗口算法维护队列时间复杂度也是O(1),算法速度达到理论下界O(n*m)(因为至少每个元素都要访问一次)

    最后(Ans=max{dp(n,j)|1<=j<=m})

    代码实现时空间复杂度可优化,可以读入一行处理一行,则a数组可以只开一行的大小,由于方程中当前行的状态只受前一行影响,dp数组可以优化为2层滚动使用。
    具体代码见下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #define inf 0x3f3f3f3f
    #define maxn 105
    #define maxm 10005
    #define _for(i,a,b) for(int i=(a);i<(b);++i)
    #define _rof(i,a,b) for(int i=(a);i>(b);--i)
    #define _rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define _per(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    
    int n,m,x,T;
    int a[maxm];//每读一层当场处理,只要一维
    int dp[2][maxm],now;//滚动成2层 
    int qz[maxm],hz[maxm];
    struct room{
    	int val;
    	int id;
    };
    deque<room> q,h;//双端队列
    int main(){
    	scanf("%d%d%d%d",&n,&m,&x,&T);
    	
    	now=0;
    	_rep(j,1,m)dp[now][j]=-inf;//初始化
    	dp[now][x]=0;
    	
    	_rep(i,1,n){
    		now^=1;
    		
    		_rep(j,1,m)scanf("%d",&a[j]);
    		
    		q.clear();
    		_rep(j,1,m){
    			qz[j]=qz[j-1]+a[j];
    			
    			int t=dp[now^1][j]-qz[j-1];
    			while(!q.empty() && q.back().val < t)q.pop_back();
    			q.push_back((room){t,j});
    			
    			while(!q.empty() && q.front().id+T<j)q.pop_front();
    			dp[now][j]=q.front().val+qz[j];//因为是第一次更新,这里不需要max 
    		}
    		
    		h.clear();
    		_per(j,m,1){
    			hz[j]=hz[j+1]+a[j];
    			
    			int t=dp[now^1][j]-hz[j+1];
    			while(!h.empty() && h.back().val < t)h.pop_back();
    			h.push_back((room){t,j});
    			
    			while(!h.empty() && h.front().id-T>j)h.pop_front();
    			dp[now][j]=max(dp[now][j], h.front().val+hz[j]);//这里需要,以判断从左边过来好还是右边过来好 
    		}
    	}
    	
    	int ans=0;
    	_rep(j,1,m)ans=max(ans,dp[now][j]);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    flex 和bison的安装和使用
    c++ map
    C++ 值传递、址传递、引用传递
    位运算
    POJ 1185 炮兵阵地 (状压DP)
    POJ 3114 Countries in War(强联通分量+Tarjan)
    Codeforces Round #262 (Div. 2) A B C
    2014多校第十场1002 || HDU 4972 A simple dynamic programming problem
    2014多校第十场1004 || HDU 4974 A simple water problem
    POJ 1144 Network(Tarjan)
  • 原文地址:https://www.cnblogs.com/de-compass/p/11233344.html
Copyright © 2020-2023  润新知