BZOJ2288: 【POJ Challenge】生日礼物
2288: 【POJ Challenge】生日礼物
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 284 Solved: 82
[Submit][Status]
Description
ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, ..., AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。
自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?
Input
第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。
第2行, N 个整数 A1, A2, ..., AN (0 ≤ |Ai| ≤ 104), 序列。
Output
一个整数,最大的和。
Sample Input
5 2
2 -3 2 -1 2
Sample Output
5
HINT
Source
题解:
感觉和数据备份这题有点像,但是又转化不过来。。。看了hzwer的题解之后恍然大悟了。。。
首先连在一块的正负相同的肯定可以看成一个点,然后我们就得到了一个正负交替的数列,并且首位两项都是正数(负数去掉)
然后如果正的项数<=m,那显然我们全部选走就获得了最大权值,否则我们需要做一点牺牲。
1)不选某些正项
2)选一些负项使得相邻的正项成为1块
记所有正数之和为sum,我们需要进行上面两种操作使得sum减掉的数最小并且满足只有m块。
我们把所有数的绝对值放入一个堆,每次取最小元素x。sum'-=x
那么如果该数原来是正的,意思是不选它;
如果是负的,意思是把它两边的正数合并。
但直接这样做是不行的,我们必须保证取负的时候两边的正的必须不被取,取正的时候两边的负的不被取。
换句话说,不能选择相邻的两个数!我们成功的将此题转化成了数据备份问题。
orz!
代码:
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 100000+5 26 27 #define maxm 500+100 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define mod 1000000007 44 45 using namespace std; 46 47 inline int read() 48 49 { 50 51 int x=0,f=1;char ch=getchar(); 52 53 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 54 55 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 56 57 return x*f; 58 59 } 60 int n,k,s,t,ans,a[maxn],b[maxn],l[maxn],r[maxn]; 61 priority_queue<pa,vector<pa>,greater<pa> >q; 62 63 int main() 64 65 { 66 67 freopen("input.txt","r",stdin); 68 69 freopen("output.txt","w",stdout); 70 71 t=read();k=read(); 72 for1(i,t)a[i]=read();while(a[t]<=0)t--; 73 s=1;while(a[s]<=0)s++; 74 for(;s<=t;s++)if((a[s]>0&&a[s-1]>0)||(a[s]<=0&&a[s-1]<=0))b[n]+=a[s];else b[++n]=a[s]; 75 for1(i,n)if(b[i]>0){ans+=b[i];k--;}else b[i]=-b[i]; 76 if(k>=0){cout<<ans<<endl;return 0;} 77 for1(i,n)l[i]=i-1,r[i]=i+1,q.push(pa(b[i],i)); 78 r[n]=0; 79 for1(i,abs(k)) 80 { 81 while(b[q.top().second]!=q.top().first)q.pop(); 82 int x=q.top().second;q.pop(); 83 ans-=b[x]; 84 if(!l[x]){b[r[x]]=inf;l[r[x]]=0;} 85 else if(!r[x]){b[l[x]]=inf;r[l[x]]=0;} 86 else 87 { 88 b[x]=b[l[x]]+b[r[x]]-b[x]; 89 b[l[x]]=b[r[x]]=inf; 90 r[l[x]=l[l[x]]]=l[r[x]=r[r[x]]]=x; 91 q.push(pa(b[x],x)); 92 } 93 } 94 cout<<ans<<endl; 95 96 return 0; 97 98 }
还有一点,之所以赋删去的点的权值为inf是为了不用手打堆,erase233