hdu6447 YJJ's Salesman(线段树/树状数组+背包+离散化)

题目

(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
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;
mapid;
struct node
{
	int x,y,v;
}e[maxm];
bool operator<(node a,node b)
{
	if(a.y!=b.y)return a.yb.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
#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;
}

 

你可能感兴趣的:(线段树(权值线段树)/树状数组)