北京清北 综合强化班 Day3
括号序列(bracket)
Time Limit:1000ms Memory Limit:128MB
题目描述
LYK有一个括号序列,但这个序列不一定合法。
一个合法的括号序列如下:
()是合法的括号序列。
若A是合法的括号序列,则(A)是合法的括号序列。
若A和B分别是合法的括号序列,则AB是合法的括号序列。
LYK想通过尽可能少的操作将这个不一定合法的括号序列变成合法的括号序列。一次修改操作是将某个字符变成另一个字符。
你能帮帮它吗?
输入格式(bracket.in)
一行一个字符串S。
输出格式(bracket.out)
一个数表示最少修改次数。
输入样例
()))
输出样例
1
样例解释
将第二个字符修改成(即可。
数据范围
对于30%的数据|S|<=10。
对于60%的数据|S|<=1000。
对于100%的数据|S|<=100000。且|S|是偶数。
思路:
从左往右扫过来遇到一个")",观察之前有没有"(",如果有就抵消掉,如果没有的话就变成"("--->X
如果我们一直这么做,扫完整个字符串之后,最终一定匹配完后剩下一堆左括号--->Y
最后ans就是X+Y/2
上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int Ma = 100010; char q[Ma]; struct MyStack { //手写栈 int top; char stack[Ma]; MyStack () { top=0; } void pop() { top--; } char push(char x) { stack[++top]=x; } } st; int main() { freopen("bracket.in","r",stdin); freopen("bracket.out","w",stdout); scanf("%s",q); int ans=0,n=strlen(q); for(int i=0; i<n; ++i) { if(q[i]=='(') st.push('('); else { if(st.top>0 && q[i]==')') st.pop(); else { st.push('('); ans++; } } } ans+=st.top>>1; printf("%d",ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <string> #include <cstring> 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++; } else o--; } else o++; } cout<<ans+o/2; return 0; }
公交车(bus)
Time Limit:1000ms Memory Limit:128MB
题目描述
LYK在玩一个游戏。
有k群小怪兽想乘坐公交车。第i群小怪兽想从xi出发乘坐公交车到yi。但公交车的容量只有M,而且这辆公交车只会从1号点行驶到n号点。
LYK想让小怪兽们尽可能的到达自己想去的地方。它想知道最多能满足多少小怪兽的要求。
当然一群小怪兽没必要一起上下车,它们是可以被分开来的。
输入格式(bus.in)
第一行三个数k,n,M。
接下来k行每行3个数xi,yi和ci。其中ci表示第i群小怪兽的小怪兽数量。
输出格式(bus.out)
一个数表示最多有多少只小怪兽能满足要求。
输入样例
3 5 3
1 3 4
3 5 2
1 5 3
输出样例
5
样例解释
第一群的3只小怪兽在1号点上车,并在3号点下车。
第二群的2只小怪兽在3号点上车,5号点下车。
数据范围
对于30%的数据小怪兽的总数不超过10只,n<=10。
对于另外30%的数据k,n<=1000。
对于100%的数据1<=n<=20000,1<=k<=50000,1<=M<=100,1<=ci<=100,1<=xi<yi<=n。
思路:
1.所有线段根据右端点进行判断,枚举每一个区间,能取就取。
2.维护一个f数组,f[i]表示这个时刻 车上已经做了多少只怪兽了(若[x,y]为z)
for(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;
需要维护:
1.区间+
2.区间查询最大值
固应该可以使用线段树 (O(klgn))
上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//听说贪心只能过样例...看来应该是真的... #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int Ma = 50055; const int Na = 20022; //lastc 当前在车上的怪兽数 //nxt 接下来应该枚举到的怪兽的id //sdown 当前枚举到的点到达应该下车的位置下车的数 int k,n,M,lastc,nxt=1,sdown,ans; //Dd[] 储存上了车的怪兽在哪里下车 //Ss[] 记录在哪个点,满足了哪些怪兽的需求 int Dd[Na],Ss[Na]; struct GG { int op,ed,cnt; //可能这里有点问题... bool operator < (const GG &qwq) const { if(op==qwq.op && ed==qwq.ed) return cnt > qwq.cnt; else { if(op==qwq.op && ed!=qwq.ed) return ed < qwq.ed; else return op < qwq.op; } } }id[Ma]; int main() { freopen("bus.in","r",stdin); freopen("bus.out","w",stdout); scanf("%d%d%d",&k,&n,&M); for(int i=1; i<=k; ++i) scanf("%d%d%d",&id[i].op,&id[i].ed,&id[i].cnt); sort(id+1, id+k+1); for(int i=1; i<=n; ++i) { //枚举n号点 lastc+=Dd[i]; //先让能下车的下车 for(int j=nxt; j<=k; ++j) { //枚举怪兽群 if(id[j].op>i) break; //对当前的i点是没有用处的,直接break nxt++; //更新接下来应该搞哪一堆怪兽 sdown=id[j].cnt; //记录上下车数 if(sdown+lastc>M) //超载 sdown=M-lastc; Dd[id[j].ed]-=sdown; Ss[i]+=sdown; lastc+=sdown; } } for(int i=1; i<=n; ++i) ans+=Ss[i]; printf("%d",ans); return 0; } /* 5 5 3 1 5 3 1 3 4 3 5 2 3 4 1 4 5 5(正) /----------------/ 5 5 7 1 5 6 1 2 1 1 3 3 2 4 4 4 5 9(误) */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int c1 = 32767; const int c2 = 453266144; const int c3 = 65535; int tree[c3+1][4],n,m,c,i,MIN,ans; struct node { int x,y,z; } A[100005]; int cmp(node i,node j) { return i.y<j.y; } void Update(int k) { tree[k<<1][2]+=tree[k][3]; tree[k<<1|1][2]+=tree[k][3]; tree[k<<1][3]+=tree[k][3]; tree[k<<1|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])>>1; if (l<=mid) work(root<<1,l,min(mid,r),k); if (r>mid) work(root<<1|1,max(mid+1,l),r,k); tree[root][2]=min(tree[root<<1][2],tree[root<<1|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])>>1,p=c2,q=c2; if (l<=mid) p=find(root<<1,l,min(mid,r)); if (r>mid) q=find(root<<1|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=c1+1; i<=c3; i++) tree[i][0]=tree[i][1]=i; for (i=c1; i>=1; i--) { tree[i][0]=tree[i<<1][0]; tree[i][1]=tree[i<<1|1][1]; } work(1,1+c1,m+c1,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+c1,A[i].y+c1); if (MIN>A[i].z) { work(1,A[i].x+c1,A[i].y+c1,-A[i].z); ans+=A[i].z; } else { work(1,A[i].x+c1,A[i].y+c1,-MIN); ans+=MIN; } } cout<<ans; return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <algorithm> #define INF 0x7fffffff using namespace std; const int M = 50005; int k,n,m,ans,p[M]; struct node { int op,ed,id; bool operator < (const node &qwq) const { return ed < qwq.ed; return op < qwq.ed; } }e[M]; int main() { scanf("%d%d%d",&k,&n,&m); for(int i=1; i<=k; i++) scanf("%d%d%d",&e[i].op,&e[i].ed,&e[i].id); sort(e+1,e+1+k); for(int i=1; i<=k; i++) { if(e[i].ed>n) break; if(p[e[i].op]>=m) continue; int minn=INF; for(int j=e[i].op; j<=e[i].ed; j++) { minn=min(m-p[j],minn); if(minn<=0) break; } if(minn>0) { if(minn>=e[i].id) { for(int j=e[i].op; j<e[i].ed; j++) p[j]+=e[i].id; ans+=e[i].id; } else { for(int j=e[i].op; j<e[i].ed; j++) p[j]+=minn; ans+=minn; } } } printf("%d\n",ans); return 0; }
解谜游戏(puzzle)
Time Limit:1000ms Memory Limit:128MB
题目描述
LYK进了一家古董店,它很想买其中的一幅画。但它带的钱不够买这幅画。
幸运的是,老板正在研究一个问题,他表示如果LYK能帮他解出这个问题的话,就把这幅画送给它。
老板有一个n*m的矩阵,他想找一个和最大的子矩阵,这个子矩阵可以由四个参数x,y,x2,y2(1<=x<=x2<=n,1<=y<=y2<=m)来表示,表示一个左上角为(x,y),右下角为(x2,y2)的矩阵。
为了让游戏更加有趣,老板给了一个常数P,他想将原来这个矩阵中恰好一个数变为P,使得这个矩阵的最大的子矩阵尽可能大。
老板想知道这个最大值是多少。
你能帮帮LYK吗?
输入格式(puzzle.in)
第一行三个数n,m,P。
接下来n行,每行m个数ai,j描述整个矩阵。
输出格式(puzzle.out)
输出一个数表示答案。
输入样例
3 3 3
-100 3 3
3 -4 3
3 3 3
输出样例
20
样例解释
改变左上角那个数。
数据范围
对于20%的数据n,m<=10。
对于40%的数据n,m<=25。
对于60%的数据n,m<=50。
对于80%的数据n,m<=100。
对于100%的数据1<=n,m<=300,|P|,|ai,j|<=1000。
思路:
1.枚举左上角 n^2 枚举右下角n^2 枚举修改的数 n^2 求和 n^2 -> n^8
2.求一个矩阵和,可以通过矩阵前缀和做到O(1)
枚举左上角 n^2 枚举右下角n^2 枚举修改的数 n^2 -> n^6
3.预处理出每个矩阵的最小值是多少。 n^4
枚举左上角 n^2 枚举右下角n^2 修改的数已知(修改最小的或者不修改) -> n^4
4.n,m<=300
假如我们不要求修改数,查询最大子矩阵
有n个数,查询最大子段和 O(n)
for (i=1; i<=n; i++) f[i]=max(f[i-1]+a[i],a[i]); //max{f[i]} = 最大子段和
要求我们修改数
修改的数一定是最小的那个数。
//f[i][0]:以i结尾并且没有数被修改过的最大和 //f[i][1]:以i结尾并且有数被修改过的最大和 //a[i] 第i列的和 for (int i=1; i<=n; 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[?][0/1]} 是答案
注意题目要求是:
恰好改变一个数
上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; const int M = 51; int n,m,p,Max=-9999999,Min=0x7fffffff; int Mi,Mj,Mk,Ml; int map[M][M],sum[M][M]; int jz[M][M][M][M]; int main() { freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); scanf("%d%d%d",&n,&m,&p); for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) { scanf("%d",&map[i][j]); sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j]; } for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) for(int k=n; k>i; --k) for(int l=m; l>j; --l) jz[i][j][k][l]=sum[k][l]-sum[i][l]-sum[k][j]+sum[i][j]; for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) for(int k=i+1; k<=n; ++k) for(int l=j+1; l<=m; ++l) if(jz[i][j][k][l]>Max) Max=jz[i][j][k][l],Mi=i,Mj=j,Mk=k,Ml=l; for(int i=Mi; i<=Mk; ++i) for(int j=Mj; j<=Ml; ++j) if(map[i][j]<Min) Min=map[i][j]; printf("%d",jz[Mi][Mj][Mk][Ml]-Min+p); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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);} } else ans=max(ans,max(dp[m][1],dp[m][0])); } } cout<<ans<<endl; } return 0; }