二叉堆(优先队列) P1792 [国家集训队]种树
作者:互联网
题目:
A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。
园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。
最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。
输入格式
输入的第一行包含两个正整数n、m。第二行n个整数Ai。
输出格式
输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。
输入 #17 3 1 2 3 4 5 6 7输出 #1
15输入 #2
7 4 1 2 3 4 5 6 7输出 #2
Error!
数据编号 | 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 |
思考:
首先想到用DP然而看到数据范围果断放弃,经过一番深思熟虑(观摩dalao题解思路)得出此题该由二叉堆+贪心可得
就是以在每个坑种树的收益建一个大根堆
然后用链表存它的前驱后继(n的后继为1,1的前驱为n)
每次取最大的收益。
那么问题来了————有可能在这个点种树比在它两边各种一棵树收益小。
但一定是两边各种一棵,因为我们建的是大根堆,先出来的一定大。
所以我们可以选这个坑种树,也可以选两边的坑各种一棵,所以我们给我们的贪心一个反悔的机会
就是选在这个坑种树以后将它两边的坑合并为一个价值为(w[l]+w[r]-w[now])的坑,代替我们选择的坑
这里非常的巧妙,就是如果选了这个坑就相当于不在那个中间的坑种树了,而是在它两边各种一棵。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 int m,n,q[200009],ans,next[200001],pre[200001]; 4 vector<int> a; 5 6 void sett(){//建立链表 7 8 for (int i=2;i<m;i++) {next[i]=i+1; pre[i]=i-1; } 9 10 next[m]=1; next[1]=2; 11 12 pre[1]=m; pre[m]=m-1; 13 14 } 15 16 void add(int i,int j){//二叉堆的插入 17 q[i]=j; 18 while (i/2&&a[q[i/2]]<a[q[i]]) { 19 swap(q[i],q[i/2]); 20 i=i/2; 21 } 22 23 } 24 25 void del(){//二叉堆的删除 26 27 swap(q[1],q[m]); m--; 28 int i=1,j=i*2; 29 while(j<=m){ 30 if (a[q[j]]<a[q[j+1]]&&j+1<=m) j+=1; 31 if (a[q[i]]<a[q[j]]) {swap(q[i],q[j]); i=j; j*=2; } else break; 32 } 33 34 } 35 36 void shan(){//链表删除 37 38 a[q[1]]=a[next[q[1]]]+a[pre[q[1]]]-a[q[1]]; 39 next[q[1]]=next[next[q[1]]]; 40 // cout<<"next[q[1]]:"<<next[q[1]]<<endl; 41 pre[pre[next[q[1]]]]=0; 42 next[pre[next[q[1]]]]=0; 43 pre[next[q[1]]]=q[1]; 44 // cout<<" next[pre[next[q[1]]]]:"<<pre[next[q[1]]]<<endl; 45 pre[q[1]]=pre[pre[q[1]]]; 46 // cout<<" pre[q[1]]:"<<pre[q[1]]<<endl; 47 next[next[pre[q[1]]]]=0; 48 pre[next[pre[q[1]]]]=0; 49 next[pre[q[1]]]=q[1]; 50 // cout<<"pre[next[pre[q[1]]]]:"<<next[pre[q[1]]]<<endl; 51 } 52 53 54 55 int main(){ 56 57 cin>>m>>n; 58 a.push_back(0); 59 sett(); 60 for (int i=1;i<=m;i++) {int x; cin>>x; a.push_back(x); add(i,i);} 61 62 //for (int i=1;i<=m;i++) cout<<q[i]<<" "<<a[q[i]]<<endl; 63 64 if (m==1&&n!=1||m!=1&&m/2<n){ cout<<"Error!"; return 0; } 65 for (int i=1;i<=n;i++){ 66 int k=0; 67 if(!(!next[q[1]]&&!pre[q[1]])) { 68 i++; 69 // cout<<q[1]<<endl; 70 k=1; 71 ans+=a[q[1]]; 72 shan(); 73 //cout<<q[1]<<" "<<pre[q[1]]<<" "<<pre[pre[q[1]]]<<" "<<next[q[1]]<<" "<<next[next[q[1]]]<<" "<<a[q[1]]<<endl; 74 } 75 i--; 76 del(); 77 // cout<<i<<"::"<<endl; 78 // for (int i=1;i<=m;i++) cout<<q[i]<<" "<<a[q[i]]<<endl; 79 // cout<<q[1]<<endl; 80 81 if (k) {m+=1; add(m,q[m]); }//插入可供反悔的选择 82 83 } 84 //cout<<next[3]<<" "<<pre[3]<<endl; 85 cout<<ans; 86 return 0; 87 88 }
标签:200,int,位置,二叉,Ai,种树,两边,P1792,国家集训队 来源: https://www.cnblogs.com/master-fiora/p/12789077.html