HDU 6447 YJJ's Salesman 【离散化+树状数组求区间最大】


传送门:HDU 6447


YJJ's Salesman
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

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



题意:
商人从(0,0)走到(109,109)途中有若干村庄,商人可以向(x,y+1)(x+1, y) (x+1, y+1)三个方向前进,当向(x+1,y+1)方向前进时有一个村庄,那么就可以获得村庄(x+1, y+1)的利润,问商人途中最多可以挣多少钱。


题解:

题意很好理解,在109 * 109 的二维矩阵中,对我们有用的只要n个村庄的坐标,其余点对最终结果没有贡献,所以我就想到了离散化这些点,按照题目要求,离散化后我们最多变成一个 105 * 105 的二维矩阵。
一开始我想用dp来做,但是105 * 105 的二维dp一定超时,那么就得降维
变成: d p [ i ] = m a x ( d p [ k ] ) + v [ i ] [ j ] dp[i]=max(dp[k])+v[i][j] dp[i]=max(dp[k])+v[i][j](1

降维的思想有点像01背包降维的思想。

我们将n个村庄离散化,并且按 y 有小到大排序,如果 y 相等就按 x 由大到小排序
s u m [ i ] sum[i] sum[i] 表示前 i 行的最大利润。

为什么要这么排序呢?因为我们一列一列的操作,每次找第一个大于等于这个村庄x的位置,求在他之前的行的最大值。
y 由小到大排序保证了我们每次求的时候,sum都是 y - 1 的最大值;x 由大到小排序是保证每次算完更新的时候,不会影响小于当前x的值的计算,都是 x - 1的量,具体可以看代码理解

用树状数组来解决区间最大值的问题。



AC代码:

#include
#include
#include
#include
#include
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=110000;
int t,n;
struct Node
{
  int x,y,v;
}a[N];
bool cmp(Node a,Node b)
{
  if(a.y==b.y) return a.x>b.x;
  return a.y<b.y;
}
int x[N];
ll sum[N];
int lowbit(int x)
{
  return x&(-x);
}
void update(int x,ll k)//更新
{
  while(x<=n)//往后更新
  {
    sum[x]=max(sum[x],k);
    x+=lowbit(x);
  }
}
ll query(int x)//求前x行最大利润
{
  ll ans=0;
  for(int i=x;i>0;i-=lowbit(i))
  {
    ans=max(ans,sum[i]);
  }
  return ans;
}
int main()
{
  scanf("%d",&t);
  while(t--)
  {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
      scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
      x[i]=a[i].x;
      sum[i]=0;
    }
    sort(a+1,a+1+n,cmp);
    sort(x+1,x+1+n);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
      int k=lower_bound(x+1,x+1+n,a[i].x)-x;//找第一个大于等于x的位置
      ll tmp=a[i].v+query(k-1);
      update(k,tmp);//如果x有小到大排序就会影响前面的值
      ans=max(ans,tmp);
    }
    printf("%I64d\n",ans);
  }
  return 0;
}

你可能感兴趣的:(树状数组)