种花 (贪心+(堆 or 线段树))

种花
【问题描述】
经过三十多个小时的长途跋涉, 小 Z 和小 D 终于到了 NOI 现场——南山南
中学。 一进校园, 小 D 就被花所吸引了( 不要问我为什么) , 遍和一旁的种花
园丁交( J) 流( L) 了起来。
他发现花的摆放竟有如此奥秘: 圆形广场共有 N 个种花的位置, 顺时针编
号 1 到 N。 并且每个位置都有一个美观度 ai, 如果在这里种花就可以得到这 ai的
美观度。 但由于地处南山土壤肥力欠佳, 两株花不能种在相邻的位置( 1 号和 N
号也算相邻位置) 。 校方一共给了 M 株花, 经过园丁的精妙摆放, 才能如此吸
引小 D。 所以现在小 D 也想知道应该如何摆这 N 株花。
【输入格式】
从文件 flower.in 中读入数据。
输入第一行包含两个整数 N,M 。
接下来一行包含 N 个正整数, 依次描述美观度 a1,a2 ,…, aN 。
【输出格式】
输出到文件 flower.out 中。
输出一个整数, 表示最佳植树方案可以得到的美观度。 如果无解输出
“Error!” , 不包含引号。
【样例输入】
7 3
1 2 3 4 5 6 7
【样例输出】
15
【数据规模与约定】
对于 50%的数据满足 N <= 3000 。
对于 100%的数据满足 M <=N <= 200000 , 1000 <=ai <= 1000。

50分代码(可采用dp寻找前i-2个的最大值)
代码如下:

if n<=3000 then
begin
fillchar(f,sizeof(f),9f);fillchar(g,sizeof(g),9f);
for i:=1 to n do
begin
f[i,0]:=0;
g[i,0]:=0;
end;
f[1,1]:=a[1];
for i:=2 to n-1 do
for j:=1 to min((i div 2+i mod 2),m) do
f[i,j]:=max(f[i,j],max(f[i-1,j],f[i-2,j-1]+a[i]));

g[2,1]:=a[2];
for i:=3 to n do
for j:=1 to min(i div 2+i mod 2,m) do
g[i,j]:=max(g[i,j],max(g[i-1,j],g[i-2,j-1]+a[i]));
if f[n-1,m]>g[n,m] then writeln(f[n-1,m])
else writeln(g[n,m]);
close(input);
close(output);
halt;
end;

100分解法
贪心+堆。
以A[i]为关键字建大根堆,用一个链表存放当前物品。
最初链表中元素是1~N,i的后继是i+1,前驱是i-1(当然,1的前驱是N,N的后继是1)。
执行M次操作,每一次操作都将堆顶元素k取出,ans+=A[k]。然后在链表中删除k的前驱pre和后继nxt,令A[k]=A[pre]+A[nxt]-A[k],并更新堆。

我一渣渣,c++调用stl的形式看不懂,只能用线段树维护区间最大值来代替堆

program df;
type point=record
a,b,c:longint;
end;
var i,j,n,m,x,y,z,k,t,len,ans:longint;
a,b,g:array[0..1000000] of longint;
f:array[0..4000000] of point;
pre,next:array[0..1000000] of longint;
function min(x,y:longint):longint;
begin
if x>y then exit(y)
else exit(x);
end;

function max(x,y:int64):int64;
begin
if x>y then exit(x)
else exit(y);
end;

procedure build(x,l,r:longint);
var mid:longint;
begin
f[x].a:=l;
f[x].b:=r;
if l=r then
begin
f[x].c:=a[l];
exit;
end;
mid:=(l+r) div 2;
build(x*2,l,mid);
build(x*2+1,mid+1,r);
f[x].c:=max(f[x*2].c,f[x*2+1].c); //维护区间最大值,f[1].c即为最大的数
end;

procedure check(x,z:longint);
var mid:longint;
begin
if f[x].a=f[x].b then
begin
f[x].c:=-maxlongint;
a[f[x].a]:=-maxlongint; //每次选择这块地之后就去除掉
exit;
end;
mid:=(f[x].a+f[x].b) div 2;
if mid>=z then check(x*2,z) else
check(x*2+1,z);
f[x].c:=max(f[x*2].c,f[x*2+1].c);
end;
procedure deal(x:longint);
var l,mid:longint;
begin
if f[x].a=f[x].b then
begin
l:=f[x].a;
ans:=ans+f[x].c;
f[x].c:=a[pre[l]]+a[next[l]]-a[l];
a[l]:=a[pre[l]]+a[next[l]]-a[l]; //不一定选择这一块地,也有可能选择左右的两块,此时加上这个就好

check(1,pre[l]);
check(1,next[l]); //这块地选过之后,左右的地就不选了

pre[l]:=pre[pre[l]];
next[pre[l]]:=l;
next[l]:=next[next[l]];
pre[next[l]]:=l; //左右往外移动一格

exit;
end;
mid:=(f[x].a+f[x].b) div 2;
if f[x*2].c>=f[x*2+1].c then deal(x*2)
else deal(x*2+1);
f[x].c:=max(f[x*2].c,f[x*2+1].c);
end;

begin
assign(input,’flower.in’);
reset(input);
assign(output,’flower.out’);
rewrite(output);
readln(n,m);

if n

posted @ 2017-08-28 17:06  Gxyhqzt  阅读(176)  评论(0编辑  收藏  举报