北京清北 綜合強化班 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
上代碼:
#include <iostream> #include <cstdio> #includeMy<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; }
#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; }std
公交車(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))
上代碼:
//聽說貪心只能過樣例...看來應該是真的... #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(誤) */瞎貪心的20分
#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; }正解
解謎遊戲(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]} 是答案
註意題目要求是:
恰好改變一個數
上代碼:
#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; }瞎搞20
#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; }正解
北京清北 綜合強化班 Day3