2017.10.3北京清北綜合強化班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|是偶數。
題解:貪心
遇到右括號,如果之前有左括號就抵消 否則 step++,把它變成左括號
最後剩下一堆左括號,之前step+左括號個數/2 即可。
本萌妹的AC代碼真好看..
#include<iostream> #include<cstdio> #include<cstring> using namespace std; string s; int len,top,ans;ACint main(){ freopen("bracket.in","r",stdin); freopen("bracket.out","w",stdout); cin>>s;len=s.length(); for(int i=0;i<len;i++){ if(s[i]==‘(‘)top++; if(s[i]==‘)‘){ if(top)top--; else ans++,top++; } } printf("%d\n",ans+top/2); fclose(stdin);fclose(stdout); 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。
題解:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; int k,n,m,ans; int c[2017]; struct GS{ int x,y,c; }gs[50002]; bool cmp(GS a,GS b){ return a.y-a.x<b.y-b.x; } void dfs(int x,int sum){ if(x==k+1){ ans=max(ans,sum); return; } int maxx=-1; for(int i=gs[x].x;i<gs[x].y;i++)maxx=max(maxx,c[i]); int gg=min(gs[x].c,m-maxx); for(int i=gs[x].x;i<gs[x].y;i++)c[i]+=gg; dfs(x+1,sum+gg); for(int i=gs[x].x;i<gs[x].y;i++)c[i]-=gg; dfs(x+1,sum); } int main(){ freopen("bus.in","r",stdin); freopen("bus.out","w",stdout); scanf("%d%d%d",&k,&n,&m); //k群小怪獸,n個站,容量m for(int i=1;i<=k;i++) scanf("%d%d%d",&gs[i].x,&gs[i].y,&gs[i].c); if(k<=30){ dfs(1,0); printf("%d\n",ans); fclose(stdin);fclose(stdout); return 0; } sort(gs+1,gs+k+1,cmp); for(int i=1;i<=k;i++){ int l=gs[i].x,r=gs[i].y; int maxx=-1; for(int h=l;h<r;h++)maxx=max(maxx,c[i]); int gg=min(gs[i].c,m-maxx); ans+=gg; for(int h=l;h<r;h++)c[i]+=gg; } printf("%d\n",ans); fclose(stdin);fclose(stdout); return 0; }玄學貪心20
首先看這麽一個經典問題,尋找盡量多的互不相交的區間。
做法是,按照右端點從小到大排序,不需要管左端點。
選擇第一條線段,然後第3.4條。那麽假設我們不選第一條線段,選擇第二條,是否會
更優呢?我們發現第二條線段比第一條凸出一部分,這就導致再選擇下一條線段時對於左
端點的要求更高。也就是必須要左端點更靠右。那麽對於本題,線段有了權值。對於一群
小怪獸來說,到底是上1只,還是2只,還是全上,還是一個都不上呢?
假設現在公交車的容積為3,假設對於第1群怪獸為線段1,有3只怪獸
我只讓2只怪獸上車,那麽還有一個空座,讓第二群怪獸上來一個
為線段2,圖上標註好了上下車距離。那麽看出第一群怪獸到底是全上
還是上2個,第二群上一個呢。發現,第一群怪獸全上是更優的,因為
同樣是運3只怪獸,如果第1群上2個,第2群比第一群的路線凸出來很多,
占更多的座位。好了,到目前為止貪心的策略為能上就上。
設f[i]為第i個點對於第P群怪獸,看看這一群能上幾只怪獸,就要求出
這群怪獸的行程的起點到終點的一段區間內的最大值。然後計算最多
能上幾只怪獸。然後修改f數組。發現這是一個區間求最大值,區間修改
的操作,線段樹可以維護。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define maxn 200005 using namespace std; int k,n,m,ans; struct Tree{ int l,r,sum,s; }tr[maxn<<2]; struct GS{ int x,y,c; bool operator < (const GS &a) const{ return y<a.y; } }a[maxn*3]; void pushup(int rt){ tr[rt].sum=max(tr[rt<<1].sum,tr[rt<<1|1].sum); return; } void pushdown(int rt){ if(!tr[rt].s)return; tr[rt<<1].s+=tr[rt].s;tr[rt<<1|1].s+=tr[rt].s; tr[rt<<1].sum+=tr[rt].s;tr[rt<<1|1].sum+=tr[rt].s; tr[rt].s=0; } void build(int rt,int l,int r){ tr[rt].l=l;tr[rt].r=r; if(l==r)return; int mid=(l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } int query(int rt,int l,int r,int qx,int qy){ pushdown(rt); if(l>=qx&&r<=qy){ return tr[rt].sum; } int mid=(l+r)>>1; if(qy<=mid)return query(rt<<1,l,mid,qx,qy); else if(qx>mid)return query(rt<<1|1,mid+1,r,qx,qy); else return max(query(rt<<1,l,mid,qx,mid),query(rt<<1|1,mid+1,r,mid+1,qy)); } void change(int rt,int l,int r,int qx,int qy,int z){ pushdown(rt); if(l>=qx&&r<=qy){ tr[rt].sum+=z; tr[rt].s+=z; return; } int mid=(l+r)>>1; if(qy<=mid)change(rt<<1,l,mid,qx,qy,z); else if(qx>mid)change(rt<<1|1,mid+1,r,qx,qy,z); else change(rt<<1,l,mid,qx,mid,z),change(rt<<1|1,mid+1,r,mid+1,qy,z); pushup(rt); } int main(){ scanf("%d%d%d",&k,&n,&m); for(int i=1;i<=k;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c); sort(a+1,a+k+1); for(int i=1;i<=k;i++){ int p=min(a[i].c,m-query(1,1,n,a[i].x,a[i].y-1)); if(p){ ans+=p; change(1,1,n,a[i].x,a[i].y-1,p); } } printf("%d\n",ans); return 0; }AC
解謎遊戲(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。
題解:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,p,ans; int sum[320][320],a[320][320]; bool init(int x,int y,int zx,int zy,int yx,int yy){ if(x>=zx&&x<=yx&&y>=zy&&y<=yy)return true; return false; } int main(){ freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); scanf("%d%d%d",&n,&m,&p); //n行m列改成p for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&a[i][j]); sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; } } ans=-0x7ffffff; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(int h=1;h<=m;h++){ for(int l=1;l<=n;l++){ for(int u=1;u+l-1<=n;u++){ for(int v=1;v+h-1<=m;v++){ int hang=u+l-1,lie=v+h-1; int all=sum[hang][lie]-sum[hang][v-1]-sum[u-1][lie]+sum[u-1][v-1]; if(init(i,j,u,v,hang,lie)) ans=max(ans,all-a[i][j]+p); else ans=max(ans,all); } } } } } } printf("%d\n",ans); fclose(stdin);fclose(stdout); return 0; }前綴和優化暴力40
正解:dp
先看一個經典問題,求一個序列的最大子段和。我們是可以O(n)求的。
dp轉移方程:f[i]=max(f[i-1]+a[i],a[i]) 對於每個數字,它可以加入前一段也可以單獨一塊。
那麽對於要求修改一次的序列呢?假如要求把a[i]->p
f[i][0]=max(f[i-1][0]+a[i],a[i])和f[i][1]=max{f[i-1][0]+p,p,f[i-1][1]+a[i]}
我們可以求出矩陣每一列的前綴和,枚舉子矩陣的上下邊界,a[i]=矩陣這
一列上下界之前的和,那麽這個問題就和我們開始的經典問題沒有差別了。
代碼:
#include<iostream> #include<cstdio> #include<cstring> #define inf 1000000000 using namespace std; int n,m,p,ans,map[310][310],sum[310][310],mn[310],a[310],dp[310][3]; int main(){ 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]=map[i][j]; sum[i][j]+=sum[i-1][j]; } } for(int tu=1;tu<=n;tu++){ for(int i=1;i<=m;i++)mn[i]=map[tu][i]; for(int td=tu;td<=n;td++){ for(int i=1;i<=m;i++)mn[i]=min(mn[i],map[td][i]); for(int i=1;i<=m;i++)a[i]=sum[td][i]-sum[tu-1][i]; dp[0][1]=-inf; for(int i=1;i<=m;i++){ dp[i][0]=max(dp[i-1][0]+a[i],a[i]); dp[i][1]=max(max(dp[i-1][1]+a[i],a[i]-mn[i]+p),dp[i-1][0]-mn[i]+p+a[i]); } if(tu==1&&td==n)ans=max(ans,dp[m][1]); else for(int i=1;i<=m;i++)ans=max(ans,max(dp[i][0],dp[i][1])); } } printf("%d\n",ans); return 0; }AC
2017.10.3北京清北綜合強化班DAY3