HDU-6447(线段树 + 离散化)YJJ's Salesman

HDU - 6447 2018中国大学生程序设计竞赛 - 网络选拔赛

题目链接:hdu-6447.

题目描述

           \;\;\;\;\; ( 0 , 0 ) (0, 0) (0,0)走到 ( 1 0 9 , 1 0 9 ) (10^{9},10^{9}) (109,109),可以向三个方向走,即 ( x , y ) (x,y) (x,y)可以到 ( x + 1 , y ) (x+1,y) (x+1,y) ( x , y + 1 ) (x, y+1) (x,y+1) ( x + 1 , y + 1 ) (x+1, y+1) (x+1,y+1),现在其中 n n n个点有价值,当以 ( x − 1 , y − 1 ) (x-1,y-1) (x1,y1) → \rightarrow ( x , y ) (x,y) (x,y)方式走到点 ( x , y ) (x,y) (x,y)时,可以得到该点的价值。
           \;\;\;\;\; 求:从 ( 0 , 0 ) (0, 0) (0,0)走到 ( 1 0 9 , 1 0 9 ) (10^{9},10^{9}) (109,109),可以获得的最大价值。
           \;\;\;\;\; 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^{5} 1n105, 0 ≤ x , y ≤ 1 0 9 0\leq x,y\leq 10^{9} 0x,y109,点的价值 0 ≤ v ≤ 1 0 3 0\leq v\leq 10^{3} 0v103.

思路

首先可以考虑哪些点有更新关系。 如下图:
@neromy
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; \;\;\;\;\; 对于点 ( x , y ) (x,y) (x,y),能更新它的点只有区域 2 2 2的点,它能更新的点只有区域 4 4 4的点。即 x k &lt; x , y k &lt; y x_{k}&lt;x,y_{k}&lt;y xk<x,yk<y 的点,可能更新 ( x , y ) (x,y) (x,y). (思路停滞处
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; \;\;\;\;\; 更新条件: x x x 坐标 和 y y y 坐标均小于它的所有点都可能将它更新。
将所有点按照 x x x 从小到大, y y y 从大到小排序。 f [ i ] f[i] f[i] 表示从 ( 0 , 0 ) 走 到 (0,0)走到 (0,0)纵坐标为 [ 1... i ] [1...i] [1...i]的点的最大价值。如果当前点是 ( x , y , v ) (x,y,v) (x,y,v),那么当前点的最大价值为 f [ y − 1 ] + v f[y-1] + v f[y1]+v . f f f 是持续更新的,当前所用 f f f的是由之前的点更新的,所以一定满足更新条件。
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; \;\;\;\;\; 用线段树维护 f f f. 但是坐标范围为 [ 0 , 1 0 9 ] [0,10^{9}] [0,109],所以首先根据坐标值 y y y 进行离散化处理,即可用线段树维护 f f f.
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; \;\;\;\;\; 设离散化后某个点的纵坐标为 y y y,则查询区间 [ 1 , y − 1 ) ] [1,y-1)] [1,y1)]的最大值 M a x Max Max M a x + v Max + v Max+v 即为当前的最大价值,再用此最大价值更新区间 [ 1 , y ] [1,y] [1,y]。(这里并不是简单的前缀
注意点:
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; \;\;\;\;\; 纵坐标是按照从大到小排列的,这样才能保证更新正确。比如现在有点 P 1 ( 1 , 1 , v 1 ) , P 2 ( 1 , 2 , v 2 ) , P 3 ( 1 , 3 , v 3 ) P_{1}(1,1,v_{1}),P_{2}(1,2,v_{2}),P_{3}(1,3,v_{3}) P1(1,1,v1),P2(1,2,v2),P3(1,3,v3),如果是按照纵坐标从小到大排序,更新点 P 3 P_{3} P3时,会用到 P 1 , P 2 P_{1},P_{2} P1,P2的值,而实际上这样更新是不对的。按照纵坐标从大到小排序就不会这样。(有点类似于01背包?)

代码



#include <bits/stdc++.h>

using namespace std;

#define LL long long 
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)

const int MAXN = 100005;

struct node {
	int l, r, Max;
	
}t[MAXN << 4];

struct point {
	int x, y, v;
	bool operator< (const point &P) const {
		return x == P.x ? y > P.y : x < P.x;
	}
	
}Point[MAXN << 4];
int lst_y[MAXN];
void push_up(int u) {
	t[u].Max = max(t[lson(u)].Max, t[rson(u)].Max);
	return;
}
void build(int l, int r, int u) {
	t[u].l = l;
	t[u].r = r;
	if(l == r) {
		t[u].Max = 0;
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, lson(u));
	build(mid + 1, r, rson(u));
	push_up(u);
	return;
}

int Query_Max(int L, int R, int u) {
	if(L <= t[u].l && R >= t[u].r) {
		return t[u].Max;
	}
	int mid = t[u].l + t[u].r >> 1, Max = 0;
	if(mid >= L) Max = Query_Max(L, R, lson(u));
	if(mid < R) Max = max(Max, Query_Max(L, R, rson(u)));
	return Max;
}
void update(int place, int x, int u) {
	if(t[u].l == t[u].r && t[u].l == place) {
		t[u].Max = max(t[u].Max, x);
		return;
	}
	int mid = t[u].l + t[u].r >> 1;
	if(mid >= place) update(place, x, lson(u));
	if(mid < place) update(place, x, rson(u));
	push_up(u);
	return;
}
int main() {
	int T, n;
	scanf("%d", &T);
	while(T--) {
		int cnt = 1;
		scanf("%d", &n);
		for(int i = 1; i <= n; i++) {
			scanf("%d%d%d", &Point[i].x, &Point[i].y, &Point[i].v);
			lst_y[cnt++] = Point[i].y;
		}
		sort(lst_y + 1, lst_y + cnt);
		int m = 0;
		m = unique(lst_y + 1, lst_y + cnt) - lst_y; // 去重 
		for(int i = 1; i <= n; i++) {
			Point[i].y = lower_bound(lst_y + 1, lst_y + m, Point[i].y) - lst_y; // 离散化 
		}
		build(1, m, 1); 
		sort(Point + 1, Point + n + 1);
		int ans = 0;
		for(int i = 1; i <= n; i++) {
			int y = Point[i].y;
			int tmp = Query_Max(1, y - 1, 1) + Point[i].v;	// f[y-1] + v
			ans = max(ans, tmp);
			update(y, tmp, 1); 	// 更新 
		}
		printf("%d\n", ans);
	}	
	return 0; 
}
/*
1
3
1 1 1
1 2 2
3 3 1

4
1 1 1
2 2 1
2 3 5
3 3 1

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值