@LittleRewriter
2017-10-08T02:21:29.000000Z
字数 3668
阅读 891
未分类
从左往右扫过来,遇到一个')'
观察之前有没有左括号
->有则抵消,无则变左(变X次)
一直持续,扫完之后最终一定匹配后剩下'('
将一半的'('变成')'(变Y次)
所以结果为X+Y/2
标程:
#include <bits/stdc++.h>using namespace std;char s[100005];int i,p,o,ans;int main(){freopen("bracket.in","r",stdin);freopen("bracket.out","w",stdout);scanf("%s",s); p=strlen(s);for (i=0; i<p; i++){if (s[i]==')'){if (o==0){o++; ans++;} elseo--;}elseo++;}cout<<ans+o/2;// system("pause");return 0;}
前置知识:找尽量多的区间互不相交
做法:按照右端点排序,能取就取,取出最多的互不相交的线段
本题是在这个基础上加了权值。下证该做法是适用的。
如图,紫色是直接取走,绿色是选一选。
显然,在同样的路线下,紫色是优于绿色的,因为这样可以留下更多的地方。
这样直接搞就是60分。
再来分析,我们是要维护一个f数组表示i时刻车上有多少只怪兽
伪代码:[x,y]加入Zfor(int i=x;i<y;++i) MAX=max(MAX,f[i]);t=min(Z,M-MAX);for (int i=X; i<Y; i++) f[i]+=t;ans+=t;cout<<ans;
因此维护的就是区间加入一些数和区间查询最大值
可以用线段树或者树状数组。
标程:
#include <cmath>#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;int tree[65536][4],n,m,c,i,MIN,ans;struct node{int x;int y;int z;};node A[100005];int cmp(node i,node j){return i.y<j.y;}void Update(int k){tree[k*2][2]+=tree[k][3];tree[k*2+1][2]+=tree[k][3];tree[k*2][3]+=tree[k][3];tree[k*2+1][3]+=tree[k][3];tree[k][3]=0;}void work(int root,int l,int r,int k){if (l==tree[root][0] && r==tree[root][1]){tree[root][2]+=k;tree[root][3]+=k;return;}Update(root);int mid=(tree[root][0]+tree[root][1])/2;if (l<=mid) work(root*2,l,min(mid,r),k);if (r>mid) work(root*2+1,max(mid+1,l),r,k);tree[root][2]=min(tree[root*2][2],tree[root*2+1][2]);}int find(int root,int l,int r){if (l==tree[root][0] && r==tree[root][1]) return tree[root][2];Update(root);int mid=(tree[root][0]+tree[root][1])/2,p=453266144,q=453266144;if (l<=mid) p=find(root*2,l,min(mid,r));if (r>mid) q=find(root*2+1,max(mid+1,l),r);return min(p,q);}int main(){freopen("bus.in","r",stdin);freopen("bus.out","w",stdout);scanf("%d%d%d",&n,&m,&c);for (i=32768; i<=65535; i++) tree[i][0]=tree[i][1]=i;for (i=32767; i>=1; i--){tree[i][0]=tree[i*2][0];tree[i][1]=tree[i*2+1][1];}work(1,1+32767,m+32767,c);for (i=1; i<=n; i++){scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z);A[i].y--;}sort(A+1,A+n+1,cmp);for (i=1; i<=n; i++){MIN=find(1,A[i].x+32767,A[i].y+32767);if (MIN>A[i].z){work(1,A[i].x+32767,A[i].y+32767,-A[i].z);ans+=A[i].z;}else{work(1,A[i].x+32767,A[i].y+32767,-MIN);ans+=MIN;}}cout<<ans;return 0;}
从左往右枚举每一个时刻,扫描一下。一旦满载,赶走最迟下车的怪兽,直到出现不满载。这样相当于我们贪出了最好的选择。
因此这样维护的就是所有怪兽从大到小,用个heap维护就行。复杂度klogM
前置知识:
最大子段和问题
f[i]为以i结尾的最大子段和
f[i]=max(f[i-1]+a[i],a[i])
而ans=max{f[i]}
最大子矩阵问题
枚举一个端点在哪一行,一个端点在另一行()
然后将每一列的和看做点,就是求最大子段和
那么对于本题,由于修改是最小的数,对于
f[i][0]表示以i结尾并且没有数被修改过的最大的和
f[i][1]表示以i结尾并且有数被修改过的最大的和
a[i]为第i列的和
则f[i][0]=max(f[i-1][0]+a[i],a[i])
f[i][1]=max(f[i-1][1]+a[i],f[i-1][0]+a[i]-MIN{i}+P,a[i]-MIN{i}+P)
结果就是MAX{f[i][0/1]}
当然这里还需要特判一下是否会恰好改变一个数
标程:
#include <cmath>#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>#include <assert.h>//判断输入是否合法using namespace std;int n,m,a[305][305],MIN[305],b[305],dp[305][2],i,j,s[305][305],ans,P,k;int main(){freopen("puzzle.in","r",stdin);freopen("puzzle.out","w",stdout);while (cin>>n){ans=-1000000000;scanf("%d%d",&m,&P); assert(1<=n && n<=300 && 1<=m && m<=300 && -1000<=P && P<=1000);for (i=1; i<=n; i++)for (j=1; j<=m; j++) {scanf("%d",&a[i][j]); assert(-1000<=a[i][j] && a[i][j]<=1000); }for (i=1; i<=n; i++)for (j=1; j<=m; j++)s[i][j]=s[i-1][j]+a[i][j];for (i=1; i<=n; i++){for (j=1; j<=m; j++) MIN[j]=a[i][j];for (j=i; j<=n; j++){for (k=1; k<=m; k++) MIN[k]=min(MIN[k],a[j][k]);for (k=1; k<=m; k++) b[k]=s[j][k]-s[i-1][k]; dp[0][1]=-1000000000;for (k=1; k<=m; k++) dp[k][0]=max(dp[k-1][0]+b[k],b[k]),dp[k][1]=max(max(dp[k-1][1]+b[k],dp[k-1][0]+b[k]-MIN[k]+P),b[k]-MIN[k]+P);for (k=1; k<m; k++) ans=max(ans,max(dp[k][0],dp[k][1]));if (i==1 && j==n){ans=max(ans,dp[m][1]); int sum=0;for (k=m; k>1; k--) {sum+=b[k]; ans=max(ans,sum);}} elseans=max(ans,max(dp[m][1],dp[m][0]));}}cout<<ans<<endl;}return 0;}