[bzoj2288][POJ Challenge]生日礼物
用堆维护双向链表来贪心。。。
数据范围显然不容许O(nm)的傻逼dp>_<。。而且dp光是状态就n*m个了。。显然没法优化
大概就会想到贪心乱搞了吧。。。一开始想贪心地通过几段小的负数把正数连接成一段,但到底是要连接在一起还是直接扔掉不好判断
然后就跑去翻题解了。。。题解讲的挺好的,连我都看懂了>_<。。题解网址:http://www.cnblogs.com/tuigou/p/4868127.html
虽然选正数和负数的意义不同,但实际的操作都是把两边的数合并起来。还有就是,对于在左端或右端的负数,把它删去后并不会减少当前选取的段数。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<cstdlib> 7 #include<cmath> 8 #define ll long long 9 using namespace std; 10 const int maxn=100233; 11 struct zs{ 12 int id; 13 }; 14 priority_queue <zs>q; 15 int a[maxn],cnt,pre[maxn<<1],next[maxn<<1],v[maxn<<1]; 16 int i,j,n,m,zsnum,ans; 17 bool del[maxn<<1]; 18 19 int ra,fh;char rx; 20 inline int read(){ 21 rx=getchar(),ra=0,fh=1; 22 while((rx<'0'||rx>'9')&&rx!='-')rx=getchar(); 23 if(rx=='-')fh=-1,rx=getchar(); 24 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh; 25 } 26 bool operator <(zs a,zs b){return abs(v[a.id])>abs(v[b.id]);} 27 int main(){ 28 n=read(),m=read();if(!m){puts("0");return 0;} 29 for(i=1;i<=n;i++)a[i]=read();cnt=0; 30 for(i=1;i<=n;v[cnt]+=a[i++]) 31 if((ll)a[i]*(ll)v[cnt]<0||!cnt)cnt++; 32 for(i=1;i<=cnt;i++)if(v[i]>0)zsnum++,ans+=v[i]; 33 if(zsnum>m){ 34 for(i=1;i<=cnt;i++)q.push((zs){i}),pre[i]=i-1,next[i]=i+1;//,printf(" %d",v[i]);puts(""); 35 pre[1]=next[cnt]=0; 36 for(i=zsnum-m;i;i--){ 37 while(!q.empty()&&del[q.top().id])q.pop();if(q.empty())break; 38 int x=q.top().id,pr=pre[x],nex=next[x]; 39 40 q.pop(),ans-=abs(v[x]),del[x]=1; 41 if(!(pr&&nex)){ 42 if(v[x]<0)i++,ans+=abs(v[x]); 43 if(pr)next[pr]=0;if(nex)pre[nex]=0; 44 } 45 else{ 46 del[pr]=del[nex]=1; 47 v[++cnt]=v[pr]+v[x]+v[nex]; 48 q.push((zs){cnt}); 49 if(pre[pr])pre[cnt]=pre[pr],next[pre[cnt]]=cnt; 50 if(next[nex])next[cnt]=next[nex],pre[next[cnt]]=cnt; 51 } 52 } 53 } 54 printf("%d\n",ans); 55 return 0; 56 }