hdu 4374 One hundred layer

第一道单调队列优化的DP,好麻烦从晚上11点看到第二天3点,终于会了。这个题目就是一传说中的是男人就下100层为模型,题目说了一个起始的位置,然后你一次最多朝一个方向(这一点很重要)走一定的步数,现在问你到最后一层能得到的最大的分数。这个题目的DP还是很明显的,当时一看这个题目就呵呵了,状态的转移很明显是dp[i][j]到达第i层第j个位置的最大获取值。当是从左边走的时候也就是从左向右走,dp[i][j]=max(dp[i-1][k]+sum[j]-sum[k-1]),同理当是从右边到达这个位置时dp[i][j]=max(dp[i-1][k]+sum[j]-sum[k+1])关于这两个方程的sum[]的含义是不同的下面会解释。从这个方程中我们可以看到dp的变化实际是由dp[i-1][k]-sum[k-1]和dp[i-1][k]-sum[k+1]决定的,那么如果你写出来这个变化过程其实每次求解下一个值得时候都是只添加了一个新的元素。然后单调队列就应运而生了,解决这个题目只需要你维护两个单调队列就可以了。下面给出维护单调队列的方法以及所用的数据结构。本人比较懒的,直接用STL里面的东西。对了先介绍一个单调队列的常用的做法,单调队列首先是元素的下标和数据的大小全部单调,一般是在队尾插入。

#include<iostream>
#include<queue>
#include<list>
#include<stdio.h>
#define inf 99999999
using namespace std;
struct node{
	int x;
	int data;
};
int map[110][10010],sum[10010],dp[110][10010];
int maxi(int a,int b)
{
	if(a>b)
		return a;
	else return b;
}
int main()
{
	int n,m,x,t,i,j;
	node fuck;
	while(scanf("%d%d%d%d",&n,&m,&x,&t)!=EOF)
	{
		list<node> q;
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
				scanf("%d",&map[i][j]);
		//初始化这个不能赋值为-1的因为元素的下限是-500
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
				dp[i][j]=-inf;
		dp[1][x]=map[1][x];
		//初始化出来第一层的时候的状态,必须得。
		for(j=x-1;j>=1&&j>=x-t;j--)
			dp[1][j]=dp[1][j+1]+map[1][j];
		for(j=x+1;j<=m&&j<=x+t;j++)
			dp[1][j]=dp[1][j-1]+map[1][j];
		for(i=2;i<=n;i++)
		{
			//队列必须每次初始化为空的
			q.clear();
			sum[0]=0;
			//从左向右走的时候
			for(j=1;j<=m;j++)
			{
				sum[j]=sum[j-1]+map[i][j];
				//队首元素是所求的元素,所以下标必然满足下面的东西,不满足出队
				while(!q.empty()&&q.front().x<j-t)
					q.pop_front();
				//有动态转移方程可知下面要进队的就是这个temp的值
				int temp=dp[i-1][j]-sum[j-1];
				//队尾如果比temp小直接出队
				while(!q.empty()&&temp>=q.back().data)
					q.pop_back();
				fuck.x=j;
				fuck.data=temp;
				//temp入队
				q.push_back(fuck);
				//对手是所求队列最大的元素所在,然后加上sum[j]。
				dp[i][j]=sum[j]+q.front().data;
			}
			//下面是对称的,这个for的过程是由其运动过程决定的。
			q.clear();
			sum[m+1]=0;
			for(j=m;j>=1;j--)
			{
				sum[j]=sum[j+1]+map[i][j];
				while(!q.empty()&&q.front().x-j>t)
					q.pop_front();
				int temp=dp[i-1][j]-sum[j+1];
				while(!q.empty()&&q.back().data<=temp)
					q.pop_back();
				fuck.x=j;
				fuck.data=temp;
				q.push_back(fuck);
				dp[i][j]=maxi(dp[i][j],sum[j]+q.front().data);
			}
		}
		int ans=dp[n][1];
		for(i=2;i<=m;i++)
			ans=maxi(ans,dp[n][i]);
		printf("%d\n",ans);
	}
	return 0;
}



 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值