题目
(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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
const int maxm=1e5+10;
int t;
int n,X[maxm];
int dat[maxn],res;
map<int,int>id;
struct node
{
int x,y,v;
}e[maxm];
bool operator<(node a,node b)
{
if(a.y!=b.y)return a.y<b.y;
return a.x>b.x;
}
void update(int p,int l,int r,int x,int v)
{
if(l==r)
{
dat[p]=max(dat[p],v);
return;
}
int mid=(l+r)>>1;
if(x<=mid)update(p<<1,l,mid,x,v);
else update(p<<1|1,mid+1,r,x,v);
dat[p]=max(dat[p<<1],dat[p<<1|1]);
}
int ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return dat[p];
int mid=(l+r)>>1;
int res=0;
if(ql<=mid)res=max(res,ask(p<<1,l,mid,ql,qr));
if(qr>=mid+1)res=max(res,ask(p<<1|1,mid+1,r,ql,qr));
return res;
}
void init()
{
id.clear();
memset(dat,0,sizeof dat);
}
int main()
{
scanf("%d",&t);
while(t--)
{
init();
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(X+1,X+n+1);
int num=unique(X+1,X+n+1)-(X+1);
for(int i=1;i<=num;++i)id[X[i]]=i;
sort(e+1,e+n+1);
for(int i=1;i<=n;++i)
{
e[i].x=id[e[i].x];
if(e[i].x==1)res=0;
else res=ask(1,1,n,1,e[i].x-1);
update(1,1,n,e[i].x,res+e[i].v);
}
printf("%d\n",ask(1,1,n,1,n));
}
return 0;
}
#include<iostream>
#include<algorithm>
#include<cstring>
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.y<b.y;
}
void update(int x,int y)
{
for(int i=x;i<=n;i+=i&-i)
tree[i]=max(tree[i],y);
}
int getmax(int x)
{
int ans=0;
for(int i=x;i>0;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;
}