
分析:
一、部分分算法:
很容易想到的是N^2的DP。。考试的时候,我就写了这个。
但是。。有一些地方没有注意到,下次要注意:①数组一定要开大一点点,防止越界;判断越界的条件一定要写在&&的前面,防止越界。
二、满分算法:sto——cstdio大神。
这个题解正确性的证明来自类似数规的思想,因为比较困难。。所以,被打上了贪心的标签。
我们可以简单地考虑第一次选择的情况。
若此时最大的数为,则在最优解中,易知若不选
,则
与
必同时被选,因为若
与
没有同时被选,则显然可以将被选的那个换成
得到至少不更差的解。
然后。。。
然后我就放弃了,但是题解没有放弃。
题解非常巧妙地使用了一个等效替换的方式,拜托了被选数不能相邻的限制。
当题解选了一个最大值后,题解在原数列中删去那三个数,并同时加入一个数,并用其替代掉原数列中
,
,
三个数,维护其在原数列中相对其他数的位置(双向链表)。
这样的话,就相当于在剩下n-2个数中再选m-1个数;并且我们可以发现ak对数量的贡献依然为1,正如一个普通的数一样。既然其满足如此多地性质,我们不禁想要假设在第二步操作中其依然满足此性质。
综上,一个完整的贪心策略(辅以堆+双向链表)便脱颖而出了。
这种神题往往是能给我们一些启示的:
②复杂的问题往往是由简单的问题变形或组合而来的,在面对复杂的问题时从简单的问题出发往往可以获取一些灵感。
③当面对题目不知所措时,发掘题目中的一些性质往往是解题的黄金之匙:诸如可逆性、对称性、等效性等等,尤其是一些奇怪的性质,绝对是破题的关键;而思考的方向也往往围绕着最值、特殊点等等进行,盲目探索只会消磨时间。
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define size 200001
int heap[size],heapsize=1,point[size],a[size],l[size],r[size];
inline void down(int x){
	int now=x,next=x<<1;
	//cout<<"D:"<<heap[x]<<endl;
	while(next<heapsize){
		if(next+1<heapsize&&a[heap[next+1]]>a[heap[next]])++next;
		if(a[heap[now]]>a[heap[next]]){/*
			for(int i=1;i<heapsize;++i)
				printf("%d(%d) ",heap[i],a[heap[i]]);
			cout<<endl;*/
			return;
		}
		//cout<<"(D)"<<heap[now]<<":"<<now<<"->"<<next<<endl;
		swap(heap[now],heap[next]);
		swap(point[heap[now]],point[heap[next]]);
		now=next,next<<=1;
	}
}
inline void up(int x){
	int now=x,next=x>>1;
	//cout<<"U:"<<heap[x]<<endl;
	while(next){
		if(a[heap[next]]>a[heap[now]]){/*
			for(int i=1;i<heapsize;++i)
				printf("%d(%d) ",heap[i],a[heap[i]]);
			cout<<endl;*/
			return;
		}
		//cout<<"(U)"<<heap[now]<<":"<<now<<"->"<<next<<endl;
		swap(heap[now],heap[next]);
		swap(point[heap[next]],point[heap[now]]);
		now=next,next>>=1;
	}
}
char * ptr=(char *)malloc(10000000);
inline void in(int &x){
	bool flag=0;
	while(*ptr<'0'||*ptr>'9')
		if(*ptr++=='-')
			flag=1;
	x=0;
	while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
	if(flag)x=-x;
}
int main(){
	freopen("nt2011_tree.in","r",stdin);
	freopen("nt2011_tree.out","w",stdout);
	int N,M,i,ans=0;
	fread(ptr,1,10000000,stdin);
	in(N),in(M);
	if(N<M<<1){
		printf("Error!");
		return 0;
	}
	for(i=0;i<N;++i)in(a[i]);
	for(i=N-2;i;--i)
		l[i]=i-1,r[i]=i+1;
	l[0]=N-1,r[0]=1,l[N-1]=N-2,r[N-1]=0;
	for(i=0;i<N;++i){
		point[i]=heapsize;
		heap[heapsize]=i;
		up(heapsize++);
		down(point[i]);
	}/*
	printf("\n-----------0----------\n");
	for(int i=1;i<heapsize;++i)
		printf("%d(%d) ",heap[i],a[heap[i]]);*/
	while(M--){
		ans+=a[heap[1]];
		//cout<<heap[1]<<":"<<l[heap[1]]<<" "<<r[heap[1]]<<endl;
		
		heap[point[l[heap[1]]]]=heap[--heapsize];
		point[heap[heapsize]]=point[l[heap[1]]];
		up(point[heap[heapsize]]);
		down(point[heap[heapsize]]);
		
		heap[point[r[heap[1]]]]=heap[--heapsize];
		point[heap[heapsize]]=point[r[heap[1]]];
		up(point[heap[heapsize]]);
		down(point[heap[heapsize]]);
		
		a[heap[1]]=a[l[heap[1]]]+a[r[heap[1]]]-a[heap[1]];
		l[heap[1]]=l[l[heap[1]]];
		r[heap[1]]=r[r[heap[1]]];
		l[r[heap[1]]]=heap[1];
		r[l[heap[1]]]=heap[1];
		down(1);
		
		/*printf("-----------%d----------\n",M);
		for(int i=1;i<heapsize;++i)
			printf("%d(%d) ",heap[i],a[heap[i]]);
		cout<<endl;*/
	}
	printf("%d",ans);
}原文地址:http://blog.csdn.net/ta201314/article/details/42362217