HDU 6447 - YJJ's Salesman - [树状数组优化DP][2018CCPC网络选拔赛第10题]

Problem Description
YJJ is a salesman who has traveled through western country. YJJ is always on journey. Either is he at the destination, or on the way to destination.
One day, he is going to travel from city A to southeastern city B. Let us assume that A is (0,0) on the rectangle map and B (109,109). YJJ is so busy so he never turn back or go twice the same way, he will only move to east, south or southeast, which means, if YJJ is at (x,y) now (0≤x≤109,0≤y≤109), he will only forward to (x+1,y), (x,y+1) or (x+1,y+1).
On the rectangle map from (0,0) to (109,109), there are several villages scattering on the map. Villagers will do business deals with salesmen from northwestern, but not northern or western. In mathematical language, this means when there is a village k on (xk,yk) (1≤xk≤109,1≤yk≤109), only the one who was from (xk?1,yk?1) to (xk,yk) will be able to earn vk dollars.(YJJ may get different number of dollars from different village.)
YJJ has no time to plan the path, can you help him to find maximum of dollars YJJ can get.

Input
The first line of the input contains an integer T (1≤T≤10),which is the number of test cases.
In each case, the first line of the input contains an integer N (1≤N≤105).The following N lines, the k-th line contains 3 integers, xk,yk,vk (0≤vk≤103), which indicate that there is a village on (xk,yk) and he can get vk dollars in that village.
The positions of each village is distinct.

Output
The maximum of dollars YJJ can get.

Sample Input
1
3
1 1 1
1 2 2
3 3 1

Sample Output
3

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

题意:

从 (0,0)(0,0) 往 (109,109)(109,109) 走,每次只能从 (x,y)(x,y) 走到 (x+1,y)(x+1,y) 或者 (x,y+1)(x,y+1) 或者 (x+1,y+1)(x+1,y+1),不能折返,不能走重复的路,

在地图上分布着一些村庄,给出村庄的坐标 (x,y)(x,y) 和 vv 值,当且仅当你从 (x?1,y?1)(x?1,y?1) 走到村庄时,你才可以获得 vv,求最大能获得多少 vv。

题解:

显然,假设我走到了某一个村庄 (x0,y0)(x0,y0),那么只有在 x≥x0+1x≥x0+1 且 y≥y0+1y≥y0+1 范围内的村庄,能使得我的 vv 值进一步增加,

换句话说,我走到了某一个村庄 (x0,y0)(x0,y0),我上一个走到的“有意义的”村庄必然是在 x≤x0?1x≤x0?1且 y≤y0?1y≤y0?1 的范围内的,

那么我就要在 x≤x0?1x≤x0?1 且 y≤y0?1y≤y0?1 的范围内找到某个村庄,我走到该村庄时,我获得的 vv值时最大的,

故,我们假设 dp[i]dp[i] 为走到村庄 ii 时能获得的最大的 vv,则状态转移方程为:

dp[i]=max(dp[j]+v[i])dp[i]=max(dp[j]+v[i]),其中村庄 jj 的坐标 (x,y)(x,y) 满足 x≤x0?1x≤x0?1 且 y≤y0?1y≤y0?1

那么,简单地说,对于每个村庄,要能 O(logn)O(log?n) 获得某区域内的最大值,同时也要能 O(logn)O(log?n)的某区域内的最大值,自然而然想到树状数组……

我们离散化纵坐标,并且从小到大枚举横坐标,用树状数组维护纵坐标为 [1,y][1,y] 区域内最大的dp[i],

1、计算到某个横坐标值上某一村庄 dp[i]dp[i],假设其纵坐标为 yy,查询 [1,y?1][1,y?1] 区域内最大值;

2、每次计算完某个横坐标值的一竖条上的所有村庄的 dp[i]dp[i],将这一竖条上所有的 dp[i]dp[i] 全部拿去更新树状数组。

附上bin巨代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3
 4 struct Node {
 5     int x, y, v;
 6     void input() {
 7         scanf("%d%d%d", &x ,&y, &v);
 8     }
 9 }node[100010];
10 bool cmp(Node a, Node b) {
11     return a.x < b.x;
12 }
13
14 int a[100010];
15 int tot;
16
17 int c[100010];
18 int lowbit(int x) {
19     return x&(-x);
20 }
21
22 void update(int i, int val) {
23     while (i <= tot) {
24         c[i] = max(c[i], val);
25         i += lowbit(i);
26     }
27 }
28 int query(int i) {
29     int res = 0;
30     while (i > 0) {
31         res = max(res, c[i]);
32         i -= lowbit(i);
33     }
34     return res;
35 }
36
37 int dp[100010];
38
39
40 int main() {
41     int T;
42     int n;
43     scanf("%d", &T);
44     while (T--) {
45         scanf("%d", &n);
46         for (int i = 0; i < n; i++)
47             node[i].input();
48         tot = 0;
49         for (int i = 0; i < n; i++) {
50             a[tot++] = node[i].y;
51         }
52         sort(a, a+tot);
53         tot = unique(a, a+tot) - a;
54         for (int i = 0; i < n; i++) {
55             node[i].y = lower_bound(a, a+tot, node[i].y) - a + 1;
56         }
57         sort(node, node+n, cmp);
58         for (int i = 0; i < n; i++) dp[i] = node[i].v;
59         for (int i = 1; i <= tot; i++) c[i] = 0;
60         int pos = 0;
61         int ans = 0;
62         for (int i = 0; i < n; i++) {
63             while (pos < i && node[pos].x < node[i].x) {
64                 update(node[pos].y, dp[pos]);
65                 pos++;
66             }
67             dp[i] = query(node[i].y - 1) + node[i].v;
68             ans = max(ans, dp[i]);
69         }
70         printf("%d\n",ans);
71     }
72     return 0;
73 }

HDU 6447 - YJJ's Salesman - [树状数组优化DP][2018CCPC网络选拔赛第10题]

原文地址:https://www.cnblogs.com/klaycf/p/9577658.html

时间: 2024-10-24 11:41:39

HDU 6447 - YJJ's Salesman - [树状数组优化DP][2018CCPC网络选拔赛第10题]的相关文章

HDU 6240 Server(2017 CCPC哈尔滨站 K题,01分数规划 + 树状数组优化DP)

题目链接  2017 CCPC Harbin Problem K 题意  给定若干物品,每个物品可以覆盖一个区间.现在要覆盖区间$[1, t]$. 求选出来的物品的$\frac{∑a_{i}}{∑b_{i}}$的最小值. 首先二分答案,那么每个物品的权值就变成了$x * b_{i} - a_{i}$ 在判断的时候先把那些权值为正的物品全部选出来, 然后记录一下从$1$开始可以覆盖到的最右端点的位置. 接下来开始DP,按照区间的端点升序排序(左端点第一关键字,右端点第二关键字) 问题转化为能否用剩

LUOGU P2344 奶牛抗议 (树状数组优化dp)

传送门 解题思路 树状数组优化dp,f[i]表示前i个奶牛的分组的个数,那么很容易得出$f[i]=\sum\limits_{1\leq j\leq i}f[j-1]*(sum[i]\ge sum[j-1])$,但是这样的时间复杂度是$O(n^2)?$,所以考虑优化,发现必须满足$sum[i]\ge sum[j-1]?$才能进行转移,那么直接离散化后用树状数组维护一个前缀和即可. #include<iostream> #include<cstdio> #include<cstr

HDU 5542 - The Battle of Chibi - [离散化+树状数组优化DP]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5542 Problem DescriptionCao Cao made up a big army and was going to invade the whole South China. Yu Zhou was worried about it. He thought the only way to beat Cao Cao is to have a spy in Cao Cao's army.

HDU 4455 Substrings --递推+树状数组优化

题意: 给一串数字,给q个查询,每次查询长度为w的所有子串中不同的数字个数之和为多少. 解法:先预处理出D[i]为: 每个值的左边和它相等的值的位置和它的位置的距离,如果左边没有与他相同的,设为n+8(看做无穷). 考虑已知w=k的答案,推w = k+1时,这时每个区间都将增加一个数,即后n-k个数会增加到这些区间中,容易知道,如果区间内有该数,那么个数不会加1,,即D[i] > k时,结果++,即查询后n-k个数有多少个D[i] > k 的数即为要加上的数,然后最后面还会损失一个区间,损失的

hdu 3450 树状数组优化dp

题意就不说了: hdu2227差不多只不过这道题不是递增  而是相邻差不超过d   其实是为都一样,  也有差别: 这道题没说范围  并且树之间的大小关系对结果有影响,所以树状数组里根据原来id来求,由于数值很大 所以还是用到离散化  这里的离散化感觉和映射差不多,离散化用来查找id: 当num[i]的id是x是,dp[i]表示方案数  很明显dp[i]=sun(dp[j],j<i&&abs(num[j]-num[i])<=d)  这里在树状数组里面就是求sum[1到num[i

bzoj3594: [Scoi2014]方伯伯的玉米田--树状数组优化DP

题目大意:对于一个序列,可以k次选任意一个区间权值+1,求最长不下降子序列最长能为多少 其实我根本没想到可以用DP做 f[i][j]表示前i棵,操作j次,最长子序列长度 p[x][y]表示操作x次后,最高玉米为y时的最长子序列长度 那么以n棵玉米分阶段,对于每个阶段 f[i][j]=max{p[k][l]}+1,  其中k=1 to j , l=1 to a[i]+j 然后用树状数组维护p[][]的最大值 1 #include<stdio.h> 2 #include<string.h&g

[BZOJ3594] [Scoi2014]方伯伯的玉米田 二维树状数组优化dp

我们发现任何最优解都可以是所有拔高的右端点是n,然后如果我们确定了一段序列前缀的结尾和在此之前用过的拔高我们就可以直接取最大值了然后我们在这上面转移就可以了,然后最优解用二维树状数组维护就行了 #include<cstdio> #include<cstring> #include<algorithm> #define N 10005 #define K 505 #define M 5505 using namespace std; inline int read() {

Testing Round #12 A,B,C 讨论,贪心,树状数组优化dp

题目链接:http://codeforces.com/contest/597 A. Divisibility time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Find the number of k-divisible numbers on the segment [a, b]. In other words you need

BZOJ3594 [Scoi2014]方伯伯的玉米田 【树状数组优化dp】

题目链接 BZOJ3594 题解 dp难题总是想不出来,, 首先要观察到一个很重要的性质,就是每次拔高一定是拔一段后缀 因为如果单独只拔前段的话,后面与前面的高度差距大了,不优反劣 然后很显然可以设出\(f[i][j]\)表示前\(i\)个玉米,第\(i\)棵必须选,且共拔高了\(j\)次的最大值 由之前的性质,我们知道\(f[i][j]\)状态中\(i\)的高度是\(h[i] + j\) 所以可以的到状态转移方程: \[f[i][j] = max\{f[k][l]\} + 1 \quad [k