种树(贪心)(较难)
2151: 种树
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 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
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
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 }