种树(贪心)

2151: 种树

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 714  Solved: 397
[Submit][Status][Discuss]

Description

A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。

Input

输入的第一行包含两个正整数n、m。第二行n个整数Ai。

Output

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。

Sample Input

【样例输入1】
7 3
1 2 3 4 5 6 7
【样例输入2】
7 4
1 2 3 4 5 6 7

Sample Output

【样例输出1】
15

【样例输出2】
Error!
【数据规模】
对于全部数据:m<=n;
-1000<=Ai<=1000
N的大小对于不同数据有所不同:
数据编号 N的大小 数据编号 N的大小
1 30 11 200
2 35 12 2007
3 40 13 2008
4 45 14 2009
5 50 15 2010
6 55 16 2011
7 60 17 2012
8 65 18 199999
9 200 19 199999
10 200 20 200000

//看了题解,思索了很久才明白,太厉害了。。。

如果没有必须要隔一个坑种一棵树的条件的话,只许排个序一直选最大的美观值就好了,

但是有这个条件,还是每次选最大美观值的坑种,直接删除边上两个坑和自己这个坑,就可能有个问题 比如 4 5 4 这样,选了 5 ,但种边上这两点美观度更好,

所以,非常关键的思路就来了,在你选择这棵坑后,删除边上的两棵,并且把自己的值改为 4+4-5 = 3 ,成为一个新的坑,然后就可以重复进行选最大的美观度的操作了

如果你后来又选了这个再造坑,说明两边的和大于中间的,这样就能反悔了,剩下的还有一些细节的原因,想清楚并不难。

实现其实也比较难,建立一个二叉最大堆,每次取出最大值,并修改值,并删除边上的坑的值,在维护堆,一个循环取数,ok

//细节部分写错,wa 很多次。。。

 1 /* ***********************************************
 2 ┆  ┏┓   ┏┓ ┆
 3 ┆┏┛┻━━━┛┻┓ ┆
 4 ┆┃       ┃ ┆
 5 ┆┃   ━   ┃ ┆
 6 ┆┃ ┳┛ ┗┳ ┃ ┆
 7 ┆┃       ┃ ┆
 8 ┆┃   ┻   ┃ ┆
 9 ┆┗━┓ 马 ┏━┛ ┆
10 ┆  ┃ 勒 ┃  ┆      
11 ┆  ┃ 戈 ┗━━━┓ ┆
12 ┆  ┃ 壁     ┣┓┆
13 ┆  ┃ 的草泥马  ┏┛┆
14 ┆  ┗┓┓┏━┳┓┏┛ ┆
15 ┆   ┃┫┫ ┃┫┫ ┆
16 ┆   ┗┻┛ ┗┻┛ ┆
17 *************************************************/
18
19 #include <iostream>
20 #include <stdio.h>
21 #include <math.h>
22 using namespace std;
23
24 const int inf = 21e8;
25 const int maxn= 2e5+2;
26
27 int a[maxn];
28 int h[maxn];
29 int pos[maxn];
30 int l[maxn],r[maxn];
31 int n,m;
32
33 void up(int u)
34 {
35     while (u>1)
36     {
37         if (a[h[u]]>a[h[u/2]])
38         {
39             swap(h[u],h[u/2]);
40             swap(pos[h[u]],pos[h[u/2]]);
41             u/=2;
42         }
43         else break;
44     }
45 }
46
47 void down(int x)//在堆中的排序的编号
48 {
49     int u;
50     while (x*2<=n)
51     {
52         if (a[h[x*2]] > a[h[x*2+1]] || x*2==n) u=x*2;
53         else u=x*2+1;
54         if (a[h[x]]<a[h[u]])
55         {
56             swap(h[x],h[u]);
57             swap(pos[h[x]],pos[h[u]]);
58             x=u;
59         }
60         else break;
61     }
62 }
63
64 int main()
65 {
66     while (scanf("%d%d",&n,&m)!=EOF)
67     {
68         int i;
69         for (i=1;i<=n;i++)
70         {
71             scanf("%d",&a[i]);
72             l[i]=i-1;r[i]=i+1;
73             h[i]=pos[i]=i;
74             up(i);
75         }
76         l[1]=n;r[n]=1;
77         if (m>n/2)
78         {
79             printf("Error!\n");
80             continue;
81         }
82         int x,ans=0;
83         for (i=1;i<=m;i++)
84         {
85             x=h[1];
86             ans+=a[x];
87             a[x]=a[l[x]]+a[r[x]]-a[x];      //变为一个新的点
88             a[l[x]]=-inf;down(pos[l[x]]);
89             a[r[x]]=-inf;down(pos[r[x]]);
90             down(1);                        //将这个新的点调整位置
91             l[x]=l[l[x]];r[x]=r[r[x]]; //删除后的左右位置变化
92             r[l[x]]=x;l[r[x]]=x;
93         }
94
95         printf("%d\n",ans);
96     }
97     return 0;
98 }

时间: 2024-10-14 00:42:32

种树(贪心)的相关文章

BZOJ 2151 种树 贪心+优先队列+HASH

题意:链接 方法:贪心+优先队列 解析: 首先裸上贪最大的肯定不对. DP可行么?可行,两个数组一个记录选一个记录不选好像差不多n^2? 不过还是想想贪心. 贪最大的为什么不对? 因为有可能它旁边的两个加起来比它更优越? 所以能否找到一点关系呢? 既然话都说到这了,两个加起来可能更优越. 所以我们在选了最大的之后是否应该回推一个值呢?代表选旁边俩. 我们发现选旁边俩是两个单位,选一个是一个单位,如果我们推回去某个值后这个值相当于对应删去一个单位选两个单位,即增加一个单位,这显然不会影响最终我们选

02day1

淘汰赛制 递推 [问题描述] 淘汰赛制是一种极其残酷的比赛制度.2n名选手分别标号1,2,3,…,2n-1,2n,他们将要参加n轮的激烈角逐.每一轮中,将所有参加该轮的选手按标号从小到大排序后,第1位与第2位比赛,第3位与第4位比赛,第5位与第6位比赛……只有每场比赛的胜者才有机会参加下一轮的比赛(不会有平局).这样,每轮将淘汰一半的选手.n轮过后,只剩下一名选手,该选手即为最终的冠军. 现在已知每位选手分别与其他选手比赛获胜的概率,请你预测一下谁夺冠的概率最大. [输入] 第一行是一个整数n(

【BZOJ 2151】 2151: 种树 (贪心+堆)

2151: 种树 Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树.园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n.并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度.但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置.值得注意的是1号和n号也算相邻位置!).最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大.如果无法将m棵树苗

【洛谷】【堆+贪心】P1484 种树

[题目描述:] cyrcyr今天在种树,他在一条直线上挖了n个坑.这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树.而且由于cyrcyr的树种不够,他至多会种k棵树.假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利. [输入格式:] 第一行,两个正整数n,k. 第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利. [输出格式:] 输出1个数,表示cyrcyr种树的最大获利. 输

洛谷P1250种树(贪心)

题目描述 一条街的一边有几座房子.因为环保原因居民想要在路边种些树.路边的地区被分割成块,并被编号成1..N.每个部分为一个单位尺寸大小并最多可种一棵树.每个居民想在门前种些树并指定了三个号码B,E,T.这三个数表示该居民想在B和E之间最少种T棵树.当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l.居民们想种树的各自区域可以交叉.你的任务是求出能满足所有要求的最少的树的数量. 写一个程序完成以下工作: 输入输出格式 输入格式: 第一行包含数据N,区域的个数(0<N≤

[国家集训队2011]种树 (神贪心~~)

/* 莫名其妙就做了集训队的题 不过..数据好水 codevs 1342 哈哈哈乱搞85 贪心的(好像有bug2333)照起点和终点 然后dp搞答案 这个应该很简单的 要滚一下数组 同桌打的暴力dp 55好像 思路一样的 就是省去了那个正确性不一定的贪心 */ #include<cstdio> #include<cstring> #define maxn 200010 using namespace std; int n,m,f[2][maxn][2],a[maxn],c[maxn

贪心算法训练(五)——种树

1. 问题描述 一条街道的一边有几座房子,因为环保原因居民想要在路边种些树,路边的地区被分割成 n 块,并被编号为 1…n,每块大小为一个单位尺寸并最多可以种一棵树,每个居民想在门前种些树并指定了三个数 b,e,t 这三个数分别表示该居民想在 b 和 e 之间最少种 t 棵树,当然,b<=e,t<=e-b+1 ,允许居民想种树的子区域可以交叉.出于资金紧缺的原因,环保部门请你求出能满足所有居民的种树要求时所需树的最少数量 2.输入格式 第一行为 n,表示区域的个数 第二行为 h,表示房子的数目

bzoj 2151: 种树【贪心+堆】

和数据备份差不多 设二元组(i,a[i]),开一个大根堆把二元组塞进去,以len排序,每次取出一个二元组 因为单纯的贪心是不行的,所以设计一个"反悔"操作. 记录二元组的前驱pr后继ne,把拿出来的二元组的len加进答案,然后把当前二元组和它的前驱后继当成一个,也就是len[x]=a[pr[x]]+a[ne[x]]-a[x],相应修改前驱后继,然后"删掉"前驱后继的二元组,这样的意思是如果再次选了修改过的二元组x,就意味着退还掉x,选择它的前驱后继,易证这样个数和a

P1484 种树 - 堆 - 贪心

这题想得脑阔疼...我只想到可以选一个点,或者不选这个点选其左右两个点的和 先来特殊情况,k=1, 然后k=2 可以发现由1到2的过程中,可以是一个本来被选的点,替换为他左右两边的点,收益增加了a[pos+1] + a[pos-1] - a[pos] 这个题是一个个选,直到选了k个,有种递推的感觉,先确定种了前面几个,再确定这一个该怎么种 然后我不会处理若有别的点也选上,并且影响到这个pos+1和pos-1的情况 事实上可以把这三个点缩在一起啊(当然不能提前缩,要在pos这个点被选时缩起来) 然