http://acm.hdu.edu.cn/showproblem.php?pid=6447
题意:
给你n个点,你只能向右,向下,向右下走,当你向右下走恰巧走到某个点时,你可以获得收益,问最大收益是多少
思路:
先离散化到1e5 * 1e5 的格点,dp[i][j]表示走到(i,j)为止的得到的最大金钱,显然dp[i][j]=max{ dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+v[i][j] },v[i][j]表示斜着走可获得的金钱。接下来优化这个dp。
显然最大值会在一个村庄取到,把带有权值的村庄(i,j)按照从上到下,从右到左的顺序更新(j从大到小时,较小的j仍保存的是上一次循环的值)。
维护f[j],f[j]表示走到第j列取得的金钱的最大值,且只在村庄处更新,所以f[j] = max( f[j] , max(f[0~j-1])+v[i][j] )。
最后答案取max{ f[0~MAX(j)] },MAX(j)表示离散化后的j的最大值 dp转移转化为区间最值查询问题,用树状数组或线段树维护即可。复杂度O(n*logn)
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const int inf=0x7fffffff;
vector<int>x,y;
struct node
{
int x,y,w;
}a[maxn];
bool cmp(node a,node b)
{
if(a.x==b.x)return a.y>b.y;
else return a.x<b.x;
}
int Max[maxn<<2];
void update(int L,int R,int pos,int x,int k)
{
if(L==R)
{
Max[x]=max(Max[x],k);
return ;
}
int mid=(L+R)/2;
if(pos<=mid)update(L,mid,pos,x*2,k);
else update(mid+1,R,pos,x*2+1,k);
Max[x]=max(Max[x*2],Max[x*2+1]);
}
int query(int L,int R,int l,int r,int x)
{
if(l<=L&&r>=R)
{
return Max[x];
}
int mid=(L+R)/2;
int ans=0;
if(l<=mid)ans=max(ans,query(L,mid,l,r,x*2));
if(r>mid)ans=max(ans,query(mid+1,R,l,r,x*2+1));
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
for(int i=0;i<=n*4;i++)
Max[i]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
x.push_back(a[i].x);
y.push_back(a[i].y);
}
sort(x.begin(),x.end());
sort(y.begin(),y.end());
x.erase(unique(x.begin(),x.end()),x.end());
y.erase(unique(y.begin(),y.end()),y.end());
for(int i=1;i<=n;i++)
{
a[i].x=lower_bound(x.begin(),x.end(),a[i].x)-x.begin()+1;
a[i].y=lower_bound(y.begin(),y.end(),a[i].y)-y.begin()+1;
}
int ans=0;
int temp;
int cnt=y.size();
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(a[i].y==1)
temp=a[i].w;
else temp=query(1,cnt,1,a[i].y-1,1)+a[i].w;
update(1,cnt,a[i].y,1,temp);
ans=max(ans,temp);
}
printf("%d\n",ans);
}
return 0;
}