hdu(3016) Man Down(线段树查询更新+dp)

这道题目可以说是游戏的简化版。

题目的大致意思是:

首先我们只有两种板,一种使能量增加,另一种却使能量减少。

最开始人物站在最高层,然后它一开始有100的生命值,它每次下落只能掉到离他最近的木板上去,当然他只能从左端点或者是右端点往下掉。

但是如果没有板满足如下情况的话,那么他就掉到最底下去了,如果此时他的能量小于等于0的话,那么他就会死亡,那么则输出-1;否则输出他所能获得能量的最大值。

现在的任务是叫你输出最大所能获得最大能量值。

思路:

1)我们要找到当前区间的分别从左右端点下去离他最近的下面那个区间

在这里可以用线段树进行维护,但是这东西还是很巧妙啊,真是难想。。。

我们这里找到的某个板的左右端点的a[i].left或是a[i].right是指它下落会落到的那块板的标号。

线段树中query操作这里其实与染色操作相类似的,就是查找到已经被染色的区间的标号。

update操作其实就是对从l,r区间进行染色,染为第i的颜色。

2)找到所能获得的最大能量值,这里要dp来做,虽然我没有想到这道题竟然可以这样转化方程:

dp方程:dp[left]=max(dp[left],dp[i]+a[a[i].left].val) ,  dp[left]等于要不是不经过第i块木板而下来的,要不是经过第i块木板下来的,这样的话就要加上第i块木板所含有的价值,显然,dp方程就是在这两个方案中寻找最大的价值。

参考来源:http://xuyemin520.is-programmer.com/posts/25907.html

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
#define maxn 111111
int dp[maxn];
struct node{
	int l,r;
	int color;
}tree[maxn*4];
struct hh{
	int xl,xr;
	int h,val;
	int left,right;
}a[maxn];
bool cmp(hh a,hh b){
	return a.h<b.h;
}
void pushdown(int v){
	int temp=v<<1;
	tree[temp].color=tree[v].color;
	tree[temp+1].color=tree[v].color;
	tree[v].color=-1;
}
void build(int l,int r,int v){
	tree[v].l=l;
	tree[v].r=r;
	tree[v].color=0;
	if(l==r) return ;
	int mid=(l+r)>>1;
	int temp=v<<1;
	build(l,mid,temp);
	build(mid+1,r,temp+1);
}
void update(int l,int r,int v,int cnt){
	if(l<=tree[v].l&&tree[v].r<=r){
		tree[v].color=cnt;
		return;
	}
	if(tree[v].color!=-1) pushdown(v);
	int temp=v<<1;
	int mid=(tree[v].l+tree[v].r)>>1;
	if(r<=mid) update(l,r,temp,cnt);
	else if(l>mid) update(l,r,temp+1,cnt);
	else{
		update(l,mid,temp,cnt);
		update(mid+1,r,temp+1,cnt);
	}
}
int query(int t,int v,int l,int r){
	if(tree[v].color!=-1){
		return tree[v].color;
	}
	int mid=(tree[v].l+tree[v].r)>>1;
	int temp=v<<1;
	if(t<=mid) query(t,temp,l,mid);
	else if(t>mid) query(t,temp+1,mid+1,r);
}
int main(){
	int n;
	while(~scanf("%d",&n)){
		memset(dp,0,sizeof(dp));
		int lmax=-1;
		for(int i=1;i<=n;i++){
			scanf("%d%d%d%d",&a[i].h,&a[i].xl,&a[i].xr,&a[i].val);
			if(lmax<a[i].xr) lmax=a[i].xr;
		}
		build(1,lmax,1);
		update(1,lmax,1,0);
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++){
			a[i].left=query(a[i].xl,1,1,lmax);
			a[i].right=query(a[i].xr,1,1,lmax);
			//printf("%d %d\n",a[i].left,a[i].right);
			update(a[i].xl,a[i].xr,1,i);          //!!!
		}
		dp[n]=100+a[n].val;
		for(int i=n;i>=1;i--){
			if(dp[i]>0){
				dp[a[i].left]=max(dp[a[i].left],dp[i]+a[a[i].left].val);
				dp[a[i].right]=max(dp[a[i].right],dp[i]+a[a[i].right].val);
			}
		}
		if(dp[0]<=0) printf("-1\n");
		else printf("%d\n",dp[0]);
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-04 17:10:36

hdu(3016) Man Down(线段树查询更新+dp)的相关文章

HDU 3874 Necklace (线段树单点更新+区间查询+离线操作)

Problem Description Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful value look the same, so if two or more balls have the same beautiful value, we just count

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

HDU 2795 Billboard (线段树单点更新)

题意:h,w,n:有一个h*w尺寸的木板,n张1*wi的海报,贴海报的位置尽量高,尽量往左,问每张海报贴的高度 看到1 <= h,w <= 10^9; 1 <= n <= 200,000,应该就是线段树了. 关键在怎么建树,这里我们对h进行分割,每个高度都有等长的w,我们从上往下贴,如果当前高度 (在同一高度上l==r)的长度可以满足wi则可以贴,否则继续往下寻找. #include <iostream> #include <stdio.h> #includ

HDU 3016 Man Down 线段树+简单DP

囧,一开始看错题意,后来才发现人是垂直下落的,被附带链接里的Man Down游戏误导了. 那就变成了一个简单的DAG模型动态规划,随意搞就ok了 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queu

ZOJ 3632 Watermelon Full of Water (线段树 区间更新 + dp)

题目大意: 让每天都能吃到西瓜.最少需要花多少钱. 思路分析: dp[pos] 就表示  要让 前i天每天都有西瓜吃,最少需要花多少钱. 那么如果你买这个西瓜的话.那么这个西瓜能吃的持续时间都要更新一下. 然后再在每个西瓜的更新部分取最小的,就可以是这个点所能得到的最小值. 其实就是 dp[i] = min (dp[i] , dp[ j - k +1] + a[j]); 但是枚举前面的时候会超时,就用线段树维护. 5 1 2 3 4 5 1 2 2 2 2 给出这组数据是说,每次买西瓜的时候,都

hdu 1540 Tunnel Warfare 线段树 单点更新,查询区间长度,区间合并

Tunnel Warfare Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1540 Description During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Gene

HDU 3016 Man Down(线段树)

HDU 3016 Man Down 题目链接 题意:是男人就下100层的游戏的简单版,每次仅仅能从两端下落.求落地最大血量 思路:利用线段树能够处理出每一个线段能来自哪几个线段.然后就是dag最长路了 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N = 100005; int n;

HDU 3577 Fast Arrangement (线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3577 题意不好理解,给你数字k表示这里车最多同时坐k个人,然后有q个询问,每个询问是每个人的上车和下车时间,每个人按次序上车,问哪些人能上车输出他们的序号. 这题用线段树的成段更新,把每个人的上下车时间看做一个线段,每次上车就把这个区间都加1,但是上车的前提是这个区间上的最大值不超过k.有个坑点就是一个人上下车的时间是左闭右开区间,可以想到要是一个人下车,另一个人上车,这个情况下这个点的大小还是不变

HDU 4902 Nice boat --线段树(区间更新)

题意:给一个数字序列,第一类操作是将[l,r]内的数全赋为x ,第二类操作是将[l,r]中大于x的数赋为该数与x的gcd,若干操作后输出整个序列. 解法: 本题线段树要维护的最重要的东西就是一个区间内所有数是否相等的标记.只维护这个东西都可以做出来. 我当时想歪了,想到维护Max[rt]表示该段的最大值,最大值如果<=x的话就不用更新了,但是好像加了这个“优化”跑的更慢了. 我想大概是因为如果两个子树最大值或者整个两个子树的数不完全相等的话,根本不能直接下传这个值或者下传gcd,因为你不知道要更