其他分享
首页 > 其他分享> > 二叉堆(优先队列) P1792 [国家集训队]种树

二叉堆(优先队列) P1792 [国家集训队]种树

作者:互联网

题目:

A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。

最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。

输入格式

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

输出格式

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

输入 #1
7 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