HUD 6447 YJJ's Salesman (dp + 树状数组优化)

题意
给你一张 109109 10 9 ∗ 10 9 的地图,你在 (0,0) ( 0 , 0 ) 位置,你只能向右或者向下或者像右下走,之后给你一些点,你如果从右下走到这些点的时候就会得到他们的值(向右走或者向下走到这些点不会得到他们的值),现在问你如何走完全图让他们的权值最大。
思路
我们可以想到没必要把所有的地图的走完,我们只想着如何把这些有值的点全部都走完之后得到一个最大值就行,具体怎么走呢?可以很显然的得到,如果有两个点A,B(B在A的右下角)倘若这两个点不在同一行或者同一列,那么我们一定能由A走到B的左上角一格C,之后由C 斜着走到B得到B点的权值,反之则不行,想到这点我们就可以想到这是一个一维的dp了,首先按照 x x 的大小去排序,之后我们 dp[i] d p [ i ] 表示的是你在第 i i 个点的时候的最大权值,那么 dp[i]=max(dp[j])+value[i] d p [ i ] = m a x ( d p [ j ] ) + v a l u e [ i ] 其中满足 第 j j 个点不和第 i i 个点在同一行同一列。那么我们现在就要想一下我们怎样维护这个 max(j) m a x ( j ) 了,很显然 n2 n 2 的去维护是肯定不行的,所以我们可以想到用线段树或者树状数组去维护这个最大值,首先离散y坐标,之后对于一个点 (x,y) ( x , y ) ,我们去树状数组中查找 (0,y1] ( 0 , y − 1 ] 区间的最大值就好,了对于限制条件:第 j j 个点不和第 i i 个点在同一行同一列我们要怎么办?首先我们可以将 y y 坐标离散之后去重这样就可以满足所有点不在同一列的情况了,那么对于我们同一行相同的我们怎么办?我们将同一行的 y y 坐标从大到小去排序,这样的话我们在查 (1,1) ( 1 , 1 ) (1,7) ( 1 , 7 ) 的时候会先更新 7 7 ,后更新 1 1 这样的话对后面的更新其实没有影响,之后这样就可以在 log l o g 的时间复杂度下得到 max(j) m a x ( j ) 了,上代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
struct node
{
    int x,y,w;
    friend bool operator < (const node &a,const node &b)
    {
        if(a.x == b.x) return a.y > b.y;
        return a.x < b.x;   
    }   
}edg[maxn];
int H[maxn] , T[maxn] , n;
void update(int pos,int v)
{
    for(int i = pos ; i < (1 << 17) ; i += i&-i) T[i] = max(T[i],v);
}
int query(int pos)
{
    int ans = 0;
    for(int i = pos ; i ; i -= i&-i) ans = max(ans,T[i]);
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int tot = 0;
        scanf("%d",&n);
        memset(T,0,sizeof(T));
        for(int i = 0 ; i < n ; i++)
        {
            scanf("%d%d%d",&edg[i].x,&edg[i].y,&edg[i].w);
            H[tot++] = edg[i].y;
        }
        sort(edg,edg+n);
        sort(H,H+tot);
        tot = unique(H,H+tot) - H;
        int ans = 0;
        for(int i = 0 ; i < n ; i++)
        {
            int pos = lower_bound(H,H+tot,edg[i].y) - H + 1;
            int te = query(pos-1) + edg[i].w;   
            update(pos,te);
            ans = max(ans,te);
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值