种树(贪心)

Posted happy_code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了种树(贪心)相关的知识,希望对你有一定的参考价值。

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 }
View Code

 

 
 
 

以上是关于种树(贪心)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ_2151_种树_贪心+堆+链表

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

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

BZOJ2151 种树 [贪心+链表]

贪心算法训练——种树

bzoj 2151: 种树贪心+堆