YJJ's Salesman (线段树优化dp+细节)

YJJ's Salesman

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


 

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中国大学生程序设计竞赛 - 网络选拔赛

Recommend

chendu   |   We have carefully selected several similar problems for you:  6447 6446 6445 6444 6443 

 

题意:
一个人起点在(0,0),只能向(x+1,y),(x,y+1) (x+1,y+1)三个方向走。给了n个坐标x,y,每个位置有价值z。每条边只能走一次。求最大价值。(只有从(x-1,y-1)到达的才能获得该点的价值)。(0 ≤ x ,y≤10^9  ) N (1 ≤ N ≤ 10^5)

思路:刚开始想到n比较小,所以可以离散化。然后就可以考虑dp了。先离散化。然后根据x由小到大,y由小到大,x相同时按照y由小到大排序。然后这时候dp【i】就代表走到(并取了i)这个时候获得的最大价值。首先我们知道(0,0)(x-1,y-1)的这块矩阵中的所有点到达(x,y)都可以获得(x,y)的价值。我们就找这块子矩阵中价值最大的值加上(x,y)的这个地方的价值。dp出了。然后就想出了直接用线段树维护前缀最大值,因为x已经由小到大排序了,这个时候就找(0-y-1)这个区间的max。这个时候就简化成一维了。然后会发现在x相同的时候,如果每一次都更新肯定是不对的,只在最后更新也是不对的,应该每个点都更新一边。这个时候用队列暂存一下这一段x相同的时候的坐标有哪些,就是依次更新。(这个地方是个坑)。

代码:

#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
long long num[maxn];
long long dp[maxn];
struct point{
    long long x;
    long long y;
    long long z;
}p[maxn];
bool cmp1(point a,point b){
    if (a.x<b.x)return 1;
    else if(a.x==b.x){
        if(a.y<b.y)return 1;
        return 0;
    }
    return 0;
}
bool cmp2(point a,point b){
    if (a.y<b.y)return 1;
    else if (a.y==b.y){
        if (a.x<b.x)return 1;
        return 0;
    }
    return 0;
}
struct xtree{
    long long l,r;
    long long maxx;
    long long mid(){
        return (l+r)/2;
    }
}tree[4*maxn];
void pushup(long long id){
    tree[id].maxx=max(tree[id*2].maxx,tree[id*2+1].maxx);
}
void build(long long id,long long l,long long r){
    tree[id].l=l;
    tree[id].r=r;
    if (l==r){
        tree[id].maxx=0;
        return ;
    }
    long long mid=tree[id].mid();
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    pushup(id);
}
void update(long long id,long long pos,long long val){
    if (tree[id].l==tree[id].r){
        tree[id].maxx=max(val,tree[id].maxx);
        return ;
    }
    long long mid=tree[id].mid();
    if (pos<=mid){
        update(id*2,pos,val);
    }
    else {
        update(id*2+1,pos,val);
    }
    pushup(id);
}
long long query(long long id,long long l,long long r){
    if (tree[id].l>=l&&tree[id].r<=r){
        return tree[id].maxx;
    }
    long long mid=tree[id].mid();
    if (r<=mid){
        return query(id*2,l,r);
    }
    else if (l>mid){
        return query(id*2+1,l,r);
    }
    else {
        return max(query(id*2,l,mid),query(id*2+1,mid+1,r));
    }
}
int main(){
    long long t;
    long long n;
    long long i,j;
    scanf("%lld",&t);
    while (t--){
        scanf("%lld",&n);
        queue<long long>qq;
        for (i=1;i<=n;i++)
            scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].z);
        sort (p+1,p+n+1,cmp2);

        num[p[1].y]=1;

        for (i=2;i<=n;i++){
            if (p[i].y!=p[i-1].y){
                num[p[i].y]=num[p[i-1].y]+1;
            }
        }
        build(1,1,num[p[n].y]);
        sort (p+1,p+n+1,cmp1);
        long long maxx=0;
        p[0].x=0;
        p[0].y=0;
        p[0].z=0;
        long long temp=0;
        for (i=1;i<=n;i++){
            if (p[i].x!=p[i-1].x)
            {
                while (!qq.empty()){
                    long long fuck=qq.front();
                    qq.pop();
                    update(1,num[p[fuck].y],dp[fuck]);
                }
                if (num[p[i].y]!=1){
                    dp[i]=p[i].z+query(1,1,num[p[i].y]-1);
                }
                else {
                    dp[i]=p[i].z;
                }
                qq.push(i);
            }
            else {
                if (num[p[i].y]!=1){
                    dp[i]=p[i].z+query(1,1,num[p[i].y]-1);
                }
                else {
                    dp[i]=p[i].z;
                }
                qq.push(i);
            }
            if (dp[i]>maxx)
                maxx=dp[i];
        }
        printf("%lld\n",maxx);
    }
}

下面是第一发WA的代码,因为没有考虑倒着更新。就是x相同的情况。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 100010
using namespace std;
long long num[maxn];
long long dp[maxn];
struct point{
    long long x;
    long long y;
    long long z;
}p[maxn];
bool cmp1(point a,point b){
    if (a.x<b.x)return 1;
    else if(a.x==b.x){
        if(a.y<b.y)return 1;
        return 0;
    }
    return 0;
}
bool cmp2(point a,point b){
    if (a.y<b.y)return 1;
    else if (a.y==b.y){
        if (a.x<b.x)return 1;
        return 0;
    }
    return 0;
}
struct xtree{
    long long l,r;
    long long maxx;
    long long mid(){
        return (l+r)/2;
    }
}tree[maxn];
void pushup(long long id){
    tree[id].maxx=max(tree[id*2].maxx,tree[id*2+1].maxx);
}
void build(long long id,long long l,long long r){
    tree[id].l=l;
    tree[id].r=r;
    if (l==r){
        tree[id].maxx=0;
        return ;
    }
    long long mid=tree[id].mid();
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    pushup(id);
}
void update(long long id,long long pos,long long val){
    if (tree[id].l==tree[id].r){
        tree[id].maxx=max(val,tree[id].maxx);
        return ;
    }
    long long mid=tree[id].mid();
    if (pos<=mid){
        update(id*2,pos,val);
    }
    else {
        update(id*2+1,pos,val);
    }
    pushup(id);
}
long long query(long long id,long long l,long long r){
    if (tree[id].l>=l&&tree[id].r<=r){
        return tree[id].maxx;
    }
    long long mid=tree[id].mid();
    if (r<=mid){
        return query(id*2,l,r);
    }
    else if (l>mid){
        return query(id*2+1,l,r);
    }
    else {
        return max(query(id*2,l,mid),query(id*2+1,mid+1,r));
    }
}
int main(){
    long long t;
    long long n;
    long long i,j;
    scanf("%lld",&t);
    while (t--){
        scanf("%lld",&n);
        for (i=1;i<=n;i++)scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].z);
        sort (p+1,p+n+1,cmp2);
        num[p[1].y]=1;
        for (i=2;i<=n;i++){
            if (p[i].y!=p[i-1].y){
                num[p[i].y]=num[p[i-1].y]+1;
            }
        }
        build(1,1,num[p[n].y]);
        sort (p+1,p+n+1,cmp1);

        int lala=p[n].x;
        int wawa=p[n].y;
        int haha=num[p[n].y];
        haha=haha+1;
        lala+=1;
        cout<<"lala:"<<lala<<endl;
        p[n+1].x=lala;
        p[n+1].y=wawa+1;
        num[p[n+1].y]=haha;
        p[n+1].z=0;
        n=n+1;
       /// cout<<p[n].y<<"     "<<p[n].x<<endl;
       /// cout<<num[p[n].y]<<"&&&"<<endl;
        long long maxx=0;
        p[0].x=0;
        p[0].y=0;
        p[0].z=0;
        long long temp=0;
        for (i=1;i<=n;i++){
            if (num[p[i].y]==1)
                dp[i]=p[i].z;
            else if (p[i].x!=p[i-1].x){
                temp=query(1,1,num[p[i].y]-1);
                dp[i]=p[i].z+temp;
                cout<<"temp:"<<temp<<endl;
            }
            else {///相同的时候不能进行查询。
              ///  temp=query(1,1,num[p[i].y]-1);
                dp[i]=p[i].z+temp;
            }
            update(1,num[p[i].y],dp[i]);
            cout<<"i:"<<i<<"    "<<"dp[i]:"<<dp[i]<<endl;

            if (dp[i]>maxx)
                maxx=dp[i];
        }
        printf("%lld\n",maxx);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值