题目
YJJ's Salesman
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1299 Accepted Submission(s): 455
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
题意
存在一个1e9*1e9的地图,一个销售员从(0,0)点要走到(1e9,1e9)的位置。设他现在所在的位置为(x,y)则他下一步走到(x+1,y),(x,y+1)或(x+1, y+1)。给出n个坐标数据,表示在(xi, yi)处且当销售员是由(xi-1, yi-1)处到达该处可以赚取vi美元。求销售员走一遍地图最高可赚到多少美元。
题解
题目给的是1e9*1e9的地图(明显数据太大),但由于仅给出n的数据(n<=1e5),因此我们需对其离散化后开辟一个1e5*1e5的地图,之后DP(转移方程dp[i][j] = max(dp[i][j], dp[i-1][x](1<=x<j)+v[i][j]))跑一遍发现时间复杂度明显超时了(其实1e5*1e5的空间也超了)。这时联想到01背包中的滚动数组,实际上我们仅需开辟一维大小为1e5的数组即可,状态方程为dp[j] = max(dp[j], dp[x]+v[i][j])。
dp[x]是区间(1,j-1)的最大值。区间最大值查询我们可以使用线段树查询,查询一次时间复杂度大概是O(logn)。所以整个算法的时间复杂度大概是O(nlogn),这样时间,空间复杂度满足条件成功AC。
唉,菜鸡比赛时还是不会,过后补题。
AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MaxN = 1e5+10;
struct Data{
int x, y, val;
};
Data data[MaxN];
bool cmp(const Data &a, const Data &b){
if(a.x == b.x){
return a.y > b.y;
}
return a.x < b.x;
}
bool Cmp(const Data &a, const Data &b){
return a.y < b.y;
}
struct node{
int l, r, val;
};
node Tree[MaxN*4];
int Hash[MaxN];
void reset(int n){
Hash[1] = 1;
sort(data+1, data+n+1, Cmp);
for(int i = 2; i <= n; i++){
if(data[i].y != data[i-1].y) Hash[i] = Hash[i-1]+1;
else Hash[i] = Hash[i-1];
}
for(int i = 1; i <= n; i++){
data[i].y = Hash[i];
}
sort(data+1, data+n+1, cmp);
}
void build(int idx, int L, int R){
Tree[idx].l = L;
Tree[idx].r = R;
if(L == R){
Tree[idx].val = 0;
return;
}
int mid = (L+R)>>1;
build(idx*2, L, mid);
build(idx*2+1, mid+1, R);
Tree[idx].val = max(Tree[idx*2].val, Tree[idx*2+1].val);
}
int quary(int idx, int L, int R){
if(L <= Tree[idx].l && Tree[idx].r <= R){
return Tree[idx].val;
}
int mid = (Tree[idx].l + Tree[idx].r)>>1;
if(R <= mid){
return quary(idx*2, L, R);
}
else if(mid < L){
return quary(idx*2+1, L, R);
}
else{
return max(quary(idx*2, L, mid), quary(idx*2+1, mid+1, R));
}
}
void updata(int idx, int L, int R, int val){
if(L <= Tree[idx].l && Tree[idx].r <= R){
Tree[idx].val = max(Tree[idx].val, val);
}
else if(R < Tree[idx].l || Tree[idx].r < L){
return;
}
else{
updata(idx*2, L, R, val);
updata(idx*2+1, L, R, val);
Tree[idx].val = max(Tree[idx*2].val, Tree[idx*2+1].val);
}
}
int main(){
int T, n;
scanf("%d", &T);
while(T--){
scanf("%d", &n);
data[0].x = 0;
data[0].y = 0;
data[0].val = 0;
for(int i = 1; i <= n; i++){
scanf("%d%d%d", &data[i].x, &data[i].y, &data[i].val);
}
reset(n);
build(1, 1, Hash[n]);
int sum = 0;
for(int i = 1; i <= n; i++){
int temp = data[i].val;
if(data[i].y!=1)
temp += quary(1, 1, data[i].y-1);
updata(1, data[i].y,data[i].y, temp);
sum = max(sum, temp);
}
printf("%d\n", sum);
}
return 0;
}
/*
5
1 3 5
1 8 1
7 5 1
7 7 3
2 3 5
*/