1. 程式人生 > >HUD 6447 YJJ's Salesman (dp + 樹狀陣列優化)

HUD 6447 YJJ's Salesman (dp + 樹狀陣列優化)

題意
給你一張 10 9 10 9 的地圖,你在

( 0 , 0 ) 位置,你只能向右或者向下或者像右下走,之後給你一些點,你如果從右下走到這些點的時候就會得到他們的值(向右走或者向下走到這些點不會得到他們的值),現在問你如何走完全圖讓他們的權值最大。
思路
我們可以想到沒必要把所有的地圖的走完,我們只想著如何把這些有值的點全部都走完之後得到一個最大值就行,具體怎麼走呢?可以很顯然的得到,如果有兩個點A,B(B在A的右下角)倘若這兩個點不在同一行或者同一列,那麼我們一定能由A走到B的左上角一格C,之後由C 斜著走到B得到B點的權值,反之則不行,想到這點我們就可以想到這是一個一維的dp了,首先按照
x 的大小去排序,之後我們 d p [ i ]
表示的是你在第 i 個點的時候的最大權值,那麼 d p [ i ] = m a x ( d p [ j ] ) + v a l u e [ i ] 其中滿足 第 j 個點不和第 i 個點在同一行同一列。那麼我們現在就要想一下我們怎樣維護這個 m a x ( j ) 了,很顯然 n 2 的去維護是肯定不行的,所以我們可以想到用線段樹或者樹狀陣列去維護這個最大值,首先離散y座標,之後對於一個點 ( x , y ) ,我們去樹狀陣列中查詢 ( 0 , y 1 ] 區間的最大值就好,了對於限制條件:第 j 個點不和第 i 個點在同一行同一列我們要怎麼辦?首先我們可以將 y 座標離散之後去重這樣就可以滿足所有點不在同一列的情況了,那麼對於我們同一行相同的我們怎麼辦?我們將同一行的 y 座標從大到小去排序,這樣的話我們在查 ( 1 , 1 ) ( 1 , 7 ) 的時候會先更新 7 ,後更新 1 這樣的話對後面的更新其實沒有影響,之後這樣就可以在 l o g 的時間複雜度下得到 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);
    }
}