离散化+dp+线段树

YJJ's Salesman

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 729    Accepted Submission(s): 204


 

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),终点是(1e9,1e9),有n个网格的点有权值,可以向右,上,右上三个方向走,但往只能右上走时才能获得该权值,要使得经过的点权值之和最大,则很容易得到状态转移方程dp[i][j]=max{dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+w[i][j]},但显然这样会内存爆炸,又由于只有n(n<=1e5)个网格有有权值的点,于是我们可以离散化一下,所谓离散化其实就是给那些点的x和y分别从小到大重新编号,使离散的点在整数集上连续起来。

然而离散之后还是发现,还是需要1e5*1e5的空间,显然不行,此时很容易想到滚动数组降维,降一维之后就变成了1e5,状态转移方程为dp[j]=max(dp[j],dp[k]+w[i][j]),{1<=k<=j-1,dp[k]=max(dp[k])},由于dp[k]为区间[1,j-1]的最大值,很容易想到用线段树或树状树组维护一下,总复杂度O(nlongn)刚好

AC code:

#include
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
const int maxn=1e5+2;
struct Node{
int x,y,w;
}node[maxn];
struct Tree{
  int l,r,m;
}tree[maxn<<2];
void pushup(int rt)
{
    tree[rt].m=max(tree[lson].m,tree[rson].m);
}
void buildtree(int l,int r,int rt)
{
    tree[rt].l=l,tree[rt].r=r;
    if(l==r)
    {
        tree[rt].m=0;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(l,mid,lson);
    buildtree(mid+1,r,rson);
    pushup(rt);
}
void update(int p,int rt,int val)
{
    int l=tree[rt].l,r=tree[rt].r;
    if(l==r)
    {
        tree[rt].m=max(val,tree[rt].m);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) update(p,lson,val);
    else update(p,rson,val);
    pushup(rt);
}
ll query(int L,int R,int rt)
{
    int l=tree[rt].l,r=tree[rt].r;
    if(L<=l&&r<=R)
    {
        return tree[rt].m;
    }
    ll ans=0;
    int mid=(l+r)>>1;
    if(L<=mid) ans=max(ans,query(L,R,lson));
    if(midb.y:a.x

最后补充一下滚动数组降维:

其实就是状态转移后前面的数组的空间不用了 ,会造成浪费,而滚动数组就是利用这部分空间

写Dp经常需要大家开高维数组,比如F[t][i][j]。有的时候转移仅需要上一维数组,如F[t-1][i][j],而F[t-2],F[t-3]都不再有用,留着占用大量空间。我们可以用滚动数组的方式节省大量空间。 本来:int F[100][100][100]; 滚动:int F[2][100][100];

使用姿势: 维护两个数字指针Now和Pre,分别表示当前在哪一维,上一维在哪一维。也就是0和1这两个下标,哪个表示的是F[t][i][j],哪个表示的是F[t-1][i][j]。

复制代码

1 //例子:
2 for (int t = 1; t <= T; t++)
3 {
4    swap(Now, Pre);/*注意这个swap用的非常精妙,不用把两个数组的值互换,仅仅互换指针就可以了。*/
5    for (int i = 1; i <= N; i++)
6        for (int j = 1; j <= M; j++)
7             F[Now][i][j] = F[Pre][i…][j…] + …;
8 }

你可能感兴趣的:(离散化+dp+线段树)