(0,0)到(1e9,1e9)的网格上有若干个点,
点(xi,yi)有一个权值vi,网格点数n<=1e5
从(0,0)出发,到(1e9,1e9),问收益最大是多少
每次可以向右走一格,或向下走一格,或向右下走一格,
收益(xi,yi)的vi,当且仅当从(xi-1,yi-1)走到(xi,yi)
归神
先将x值离散化一下,缩成1-1e5的范围,
然后点按y增序排列,如果y相同就按x降序排列
每次对行dp,这一行转移到下一行,降到一维的情况
考虑第一行,由于没有上一行,所以每个x点都能收获价值
考虑下一个有点的行的某个点(xi,yi),
其答案一定从其左上角的矩形取得,
又根据dp是按照行dp的,矩形最下面一行是最优的结果,
所以dp[xi]=max(dp[xi],RMQ(1,xi-1)+vi),类似背包取不取的操作
而dp[xi]是无法从同一行左边那个值更新的,所以xi要根据降序排列
这样先更新的xi不会对后续的xi左边的点造成影响
每次维护一行的最大值,每行从右向左扫更新最大值,
然后下一行xi处的最大值从上一行[1,xi-1]的最大值转移而来,
这样最后一行的最大值就是答案
①特判一下,位于最左边的点
②背包顺序的重要性
③map离散化,如果询问Q过多导致时间卡这个logn的话,
加上一个id对pair离散化,这样就把logn去掉了
不过这题连排序都是nlogn,映射自然也就nlogn没啥事
树状数组处理类似这种逆序对顺序对的写法很好
这样写二维降一维的dp就非常方便
所以更新一下树状数组的版本,
而且树状数组不用特判0
此外,树状数组只用开一倍的空间
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e5+10;
int tree[maxn],X[maxn];
int t,n;
struct node
{
int x,y,v;
}e[maxn];
bool operator<(node a,node b)
{
return (a.y==b.y)?a.x>b.x:a.y0;i-=i&-i)
ans=max(ans,tree[i]);
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(tree,0,sizeof tree);
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
X[i]=e[i].x;
}
sort(e+1,e+n+1);
sort(X+1,X+n+1);
for(int i=1;i<=n;++i)
{
int pos=lower_bound(X+1,X+n,e[i].x)-X;
int v=getmax(pos-1);
update(pos,v+e[i].v);
}
printf("%d\n",getmax(n));
}
return 0;
}