您的位置:首页 > 理论基础 > 数据结构算法

HDU 4719 浅谈数据结构优化双关键字动态规划

2017-09-27 15:42 676 查看


世界真的很大

这两天没怎么写题了,主要在考试

今天好不容易腾出一点时间,调了一下之前剩下来的这道题

既然这周在考数据结构,我自己在做DP,那就来一点数据结构优化的DP好了

本着这样的想法做了这道题

但是由于考试一直没有时间调。。

看题先:

description:

给定一排人和他们对应的身高,让你把他们分成几段,保证每一段的末尾的人的身高单增,每一种方案有一个分数,它的分数是SUM(b[k]^2-b[k-1] ) b[k] 为第k堆最右一个人的身高 要求最高的分数。

input

The first line has a number T (T <= 10) , indicating the number of test cases.

For each test case, first line has two numbers N and L (1 <= L <= N <= 10 5), as described above.

Then comes a single line with N numbers, from H1 to Hn, they are the height of each soldier in the line, from left to right. (1 <= H i <= 10 5)

output

For test case X, output “Case #X: ” first, then output the best score.

练习读一下英文吧233

首先一段序列分成几段求最大权值这种东西,还是见的多了

这样的DP方程一般都是什么f(i,j)表示前i个分成j段的最大权值什么的

那既然这道题不要求多少段,那么j这一维就可以省掉了,用f(i)来表示前i个人分成“管他多少段”的最大权值

考虑怎么转移,这个思路还是很好想,就是f(i)=max(f(j)+b(i)^2+b(j)),这样转移就好了,转移时只要保证b(i) > b(j)就行了

但是这样是n^2的,因为要枚举i和j,考虑优化

考虑动态规划用数据结构优化,只能优化“填表法”

所谓“填表法”就是每次由之前的信息来推出现在的答案,比如这道题想要知道f(i)是需要枚举其之前的f(j)的。

而对应的“刷表法”是通过现在已知的答案去更新所有由这个状态能推导到的状态的答案

只有填表法能用数据结构优化,因为数据结构所优化的只有数据的“收集”,而不是数据的“分散”。

而这道题之所以能够优化,就是因为其是填表法

用线段树或者其他数据结构保存其之前一段的f值,每次选最大的内个去更新

但是还剩一个问题

每次必须选b值比i小的j,而线段树没法在这些点里面更新最小值

这样相当于线段树选着时有两个关键字b和f,在满足第一关键字比i小的情况下,找一个f最大的

这样其实是有一个套路的,既然反正只有第一关键字满足条件的能更新,那就干脆按照第一关键字排序

然后继续更新

完整代码:

#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long dnt;

const int INF=0x3f3f3f3f;

struct people
{
dnt h;
int id;
}ple[200010];

struct node
{
dnt sum;
node *ls,*rs;
void update()
{
sum=max(ls->sum,rs->sum);
}
}pool[4000010],*tail=pool,*root;

int T,n,L;
dnt f[1000010];

bool cmp(const people &a,const people &b)
{
if(a.h==b.h) return a.id>b.id;
return a.h<b.h;
}

node *build(int lf,int rg)
{
node *nd=++tail;
if(lf==rg)
{
nd->sum=-1;
nd->ls=nd->rs=0;
return nd;
}
int mid=(lf+rg)>>1;
nd->ls=build(lf,mid);
nd->rs=build(mid+1,rg);
nd->update();
return nd;
}

void modify(node *nd,int lf,int rg,int pos,dnt delta)
{
if(lf==rg)
{
nd->sum=delta;
return ;
}
int mid=(lf+rg)>>1;
if(pos<=mid) modify(nd->ls,lf,mid,pos,delta);
else modify(nd->rs,mid+1,rg,pos,delta);
nd->update();
}

dnt query(node *nd,int lf,int rg,int L,int R)
{
if(L<=lf && rg<=R)
return nd->sum;
int mid=(lf+rg)>>1;
dnt rt=-INF;
if(L<=mid) rt=max(rt,query(nd->ls,lf,mid,L,R));
if(R>mid) rt=max(rt,query(nd->rs,mid+1,rg,L,R));
return rt;
}

void init()
{
root=0;
tail=pool;
memset(f,-1,sizeof(f));
}

int main()
{
scanf("%d",&T);
for(int e=1;e<=T;e++)
{
init();
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++)
cin >> ple[i].h,ple[i].id=i;
sort(ple+1,ple+n+1,cmp);
root=build(0,n);
modify(root,0,n,0,0);
for(int i=1;i<=n;i++)
{
dnt tmp=query(root,0,n,max(0,ple[i].id-L),ple[i].id-1);
if(tmp>=0)
{
f[ple[i].id]=ple[i].h*ple[i].h+tmp;
modify(root,0,n,ple[i].id,f[ple[i].id]-ple[i].h);
}
if(ple[i].id==n) break ;
}
printf("Case #%d: ",e);
if(f
>0)
cout << f
<< endl;
else printf("No solution\n");

}
return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: