hdu 6447 YJJ's Salesman(离散化+树状数组优化dp) 题意 思路 代码

有一天,你想从城市A走到城市B,假设城市A在正方形$(0,0)$点,而城市B在正方形$(10^9,10^9)$点,你是能向东,向南或者向东南走,也就是说,假设你站在$(x,y)$点,你只能向$(x+1,y),(x,y+1)和(x+1,y+1)$这三个方向走。在这个矩阵的地图上,还散落着一些小村庄,村庄k坐落的位置为$(x_k,y,_k)$,如果你能从$(x_k-1,x_y-1)$的位置走到村庄k,那么你就可以得到$v_k$的金钱,问你可以得到的最大的金钱数量为多少?

输入

$T$组输入数据,范围$(1≤N≤10)$ 

$N$个村庄,范围$(1≤N≤10^5) $

接下来有$N$行,每行有三个数为$x_k,y_k,v_k(1≤v_k≤10^3)$

输出

可以得到的最大的金钱数

思路

 首先我们可以明显的看到这是一个dp,状态转移方程为$dp[i][j]=max(dp[i-1][j-1]+v[i][j],dp[i-1][j],dp[i][j-1])$其中$dp[i][j]$表示的是在$(i,j)$位置得到的最大金钱数量。但是同时也会发现,这个地图有$(10^9,10^9)$那么大,直接用上面的状态转移方程根本不可能啊!转念一想,我们可以利用滚动数组,将二维的变为一维的,此时的状态转移方程为$dp[j]=max(dp[k])+v[i][j](1≤k≤j-1)$,其中$dp[j]$表示在第$j$列时得到的最大金钱数量,但是此时还是$10^9$的数组。

再定睛一看,虽然地图很大,但是村庄很少,十分稀疏,最多只有$10^5$个,所以将横坐标离散化,这样数据范围就大大的降低。

对于找$j-1$个$dp$的最大,可以利用树状数组维护前缀最大值,我们知道树状数组$tree[x]$放置的是从$x$开始的前$lowbit(x)$个数的和,那么我们可以将$tree[x]$变为从$x$开始的前$lowbit(x)$个数的最大值是多少,这时查找最大值的时间复杂度为$log^n$,整体算法的时间复杂度为$nlog^n$

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int inf = 0x3f3f3f3f;
int T;
int n;
int num = 0;
int tree[N];

struct node
{
    int x,y;
    int v;
}data[N];

bool cmp(node a,node b)
{
    if(a.x<b.x) return true;
    else if(a.x==b.x)
    {
        if(a.y>b.y) return true;
        else return false;
    }
    else return false;
}

int lowbit(int x)
{
    return x&-x;
}

void add(int x,int k)
{
    while(x<=num)
    {
        tree[x] = max(tree[x],k);
        x = x + lowbit(x);
    }
}

int getmax(int x)
{
    int maxx = -inf;
    while(x>=1)
    {
        maxx = max(maxx,tree[x]);
        x = x - lowbit(x);
    }
    return maxx;
}


int main()
{
    cin>>T;
    while(T--)
    {
        cin>>n;
        memset(tree,0,sizeof(tree));
        vector <int> v;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&data[i].x,&data[i].y,&data[i].v);
            v.push_back(data[i].y);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        sort(data,data+n,cmp);
        int ans = -inf;
        num = v.size();
        for(int i=0;i<n;i++)
        {
            int j = upper_bound(v.begin(),v.end(),data[i].y)-v.begin();
            int w = data[i].v;
            tree[j] = ( j!=1 ? max(getmax(j-1)+w,tree[j]) : max(tree[j],w));
            add(j,tree[j]);
            ans = max(ans,tree[j]);
        }
        cout<<ans<<endl;
    }
    return 0;
}