【NOIp模拟赛】种花
【问题描述】
经过三十多个小时的长途跋涉,小Z和小D终于到了NOI现场——南山南中学。一进校园,小D就被花所吸引了(不要问我为什么),遍和一旁的种花园丁交(J)流(L)了起来。
他发现花的摆放竟有如此奥秘:圆形广场共有 N个种花的位置,顺时针编号1到N。并且每个位置都有一个美观度,如果在这里种花就可以得到这的美观度。但由于地处南山土壤肥力欠佳,两株花不能种在相邻的位置(1号和N号也算相邻位置)。校方一共给了 M株花,经过园丁的精妙摆放,才能如此吸引小D。所以现在小D也想知道应该如何摆这 N株花。
【输入格式】
从文件 flower.in 中读入数据。
输入第一行包含两个整数N,M 。
接下来一行包含N个正整数,依次描述美观度。
【输出格式】
输出到文件 flower.out 中。
输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。
【样例输入】
7 3
1 2 3 4 5 6 7
【样例输出】
15
【数据规模与约定】
对于50%的数据满足。
对于100%的数据满足,。
分析
贪心+堆
我们先把所有点放入堆中,每次取出美观度最大的。如果它不是最优解,那么它旁边的两个肯定是。
这个可以用反证法来证明,如果它旁边的两个不是最优解,那么肯定可以选它,因为他是最大的。
如果他旁边有一个是最优解一个不是最优解,这种情况肯定是不存在的,设它左边的不是最优解,左边肯定不影响,它现在是美观度最大的,选右边的还不如选它。
每次取出最大之后讲
V=Vpre+Vnext−V加入入堆,最后删掉Pre和Next,每次用堆维护,取出最大值即可。
上面的公式是很巧妙的,也就是说如果它不是最优解还可以再选一次,只不过这次会把之前的v给减掉,加上它两边的和,而相比之前的那一次正好选了两次。
代码
手打的堆有点长不过速度比STL快。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=200000+5; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m,heap_size,ans; int a[maxn],nxt[maxn],pre[maxn],heap[maxn]; bool vis[maxn]; inline void put(int x) { int now,nxt; heap[++heap_size]=x; now=heap_size; while(now>1) { nxt=now>>1; if(a[heap[now]]<=a[heap[nxt]]) break; int t=heap[now]; heap[now]=heap[nxt]; heap[nxt]=t; now=nxt; } } inline int get() { int now=1,nxt,res=heap[1]; heap[1]=heap[heap_size--]; while((now<<1)<=heap_size) { nxt=now<<1; if(nxt<heap_size&&a[heap[nxt+1]]>a[heap[nxt]]) nxt++; if(a[heap[now]]>=a[heap[nxt]]) break; int t=heap[now];heap[now]=heap[nxt]; heap[nxt]=t; now=nxt; } return res; } int main() { freopen("flower.in","r",stdin); freopen("flower.out","w",stdout); n=read(); m=read(); if((m<<1)>n) { printf("Error!"); return 0;} for(int i=1;i<=n;i++) { a[i]=read(); put(i);} for(int i=1;i<n;i++) nxt[i]=i+1; for(int i=2;i<=n;i++) pre[i]=i-1; nxt[n]=1; pre[1]=n; for(int i=1;i<=m;i++) { int x=get(); while(vis[x]) x=get(); ans+=a[x]; a[x]=a[pre[x]]+a[nxt[x]]-a[x]; vis[pre[x]]=1; vis[nxt[x]]=1; put(x); nxt[x]=nxt[nxt[x]]; pre[x]=pre[pre[x]]; pre[nxt[x]]=x; nxt[pre[x]]=x; } printf("%d\n",ans); return 0; }