A .Regular Bracket Sequence
题意:给定“((” , “()” , “)(”, “))”四种,问是否可以组成合法括号匹配
思路:设四种是ABCD,B可以不用管,而C在A或者D存在时可以不考虑,然后就是A=D。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; ll A,B,C,D,ans; int main() { scanf("%lld%lld%lld%lld",&A,&B,&C,&D); ans=B; if(A||D) ans+=C; ans+=2LL*min(A,D); if(ans==A+B+C+D) puts("1"); else puts("0"); return 0; }
B .Discounts
题意:给定N个物品,Q次询问,每次询问给定P,表示你可以选P个,最便宜的那个不用付款,问最少付款额。
思路:排序,贪心。
#include<bits/stdc++.h> #define ll long long #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=2000010; ll a[maxn],sum,ans; int main() { int N,M,x; scanf("%d",&N); rep(i,1,N) scanf("%lld",&a[i]),sum+=a[i]; sort(a+1,a+N+1); scanf("%d",&M); rep(i,1,M) { scanf("%d",&x); printf("%lld ",sum-a[N+1-x]); } return 0; }
C .Painting the Fence
题意:给定长度为N个点,M条线段,问选择M-2条,最多可以覆盖多少个点(N,M<5000)
思路:首先如果点被覆盖数大于2,不用考虑了,一定被覆盖。所以先求出被1个线段覆盖的,被2覆盖的,然后枚举不选的线段,前缀和更新答案即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=5100; int sum[maxn],num[3][maxn],ans,tot; pair<int,int>s[maxn]; int main() { int N,M; scanf("%d%d",&N,&M); rep(i,1,M){ scanf("%d%d",&s[i].first,&s[i].second); sum[s[i].first]++; sum[s[i].second+1]--; } rep(i,1,N) sum[i]+=sum[i-1]; rep(i,1,N){ if(sum[i]) tot++; num[1][i]+=num[1][i-1]; num[2][i]+=num[2][i-1]; if(sum[i]==1) num[1][i]++; if(sum[i]==2) num[2][i]++; } sort(s+1,s+M+1); rep(i,1,M) rep(j,i+1,M){ if(s[j].first>s[i].second) ans=max(ans,tot-(num[1][s[i].second]-num[1][s[i].first-1])-(num[1][s[j].second]-num[1][s[j].
first-1])); else if(s[j].second<=s[i].second) ans=max(ans,tot-(num[1][s[j].first-1]-num[1][s[i].first-1])-(num[1][s[i].second]-num[1][s[j].second])-(num[2][s
[j].second]-num[2][s[j].first-1])); else ans=max(ans,tot-(num[1][s[j].first-1]-num[1][s[i].first-1])-(num[1][s[j].second]-num[1][s[i].second])-(num
[2][s[i].second]-num[2][s[j].first-1])); } printf("%d\n",ans); return 0; }
D .Stressful Training
题意:有N台电脑,每台电脑初始电量a[],每单位时间掉电b[],现在给一个充电器,这个充电器同一单位时间只能给一个电脑充电,问每单位时间至少充多少,满足每台电脑都至少坚持K时间。
思路:二分X,二分之后这么写呢,是线性的呢。 求出每个人最晚的一些充电时间,然后总量不大于K,然后就是线性的向前摊平,如果同一时间个数大于1,则false。
这两个限制使得每次二分是线性的。
#include<bits/stdc++.h> #define ll long long #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=200010; ll a[maxn],b[maxn],ans=-1;int N,K,vis[maxn]; bool check(ll x) { int cnt=0; rep(i,1,K) vis[i]=0; rep(i,1,N){ ll L=a[i]; while(L-b[i]*(K-1)<0){ int t=L/b[i]+1; if(L/b[i]+1<=K) vis[L/b[i]+1]++; cnt++; if(cnt==K) return false; L+=x; } } for(int i=K;i>1;i--) { if(vis[i]) vis[i-1]+=vis[i]-1; } if(vis[1]>1) return false; return true; } int main() { ll L=0,R=1e18,Mid; scanf("%d%d",&N,&K); rep(i,1,N) scanf("%lld",&a[i]); rep(i,1,N) scanf("%lld",&b[i]); while(L<=R){ Mid=(L+R)/2; if(check(Mid)) R=Mid-1,ans=Mid; else L=Mid+1; } printf("%lld\n",ans); return 0; }
E .Knapsack
题意:给定8种物体的个数a[],重量分别对应1到8,给定W,问选处的不超过W的最大重量。 (W<1e18,a<1e16)
思路:二进制拆分物体,然后暴力背包,可能会空间爆炸,用ans减去没用的一些空间就够了。
这样的话,物体要从大到小排序,这样才能保证map的空间,以及减枝效果。
(这是比较暴力的做法了,有更优美的解法
#include<bits/stdc++.h> #define ll long long using namespace std; map<ll,bool>mp,tmp; map<ll,bool>::iterator it; const int maxn=20000; ll ans,W,a[maxn],v[maxn],sum[maxn]; int tot,cnt; int main() { scanf("%lld",&W); for(int i=1;i<=8;i++){ scanf("%lld",&a[i]); if(!a[i]) continue; cnt++; ll t=1; ans=max(ans,min(W/i,a[i])*i); while(1){ tot++; v[tot]=min(t,a[i])*i; a[i]-=min(t,a[i]); t=t*2; if(!a[i]) break; } } if(cnt==1){ printf("%lld\n",ans); return 0; } sort(v+1,v+tot+1); reverse(v+1,v+tot+1); for(int i=tot;i>=1;i--) sum[i]=sum[i+1]+v[i]; mp[0]=true; for(int i=1;i<=tot;i++){ tmp.clear(); for(it=mp.begin();it!=mp.end();it++){ ll x=it->first; tmp[x]=true; if(x+v[i]<=W) tmp[x+v[i]]=true; } mp.clear(); for(it=tmp.begin();it!=tmp.end();it++){ if(it->first+sum[i+1]<ans) continue; mp[it->first]=true; ans=max(it->first,ans); } } printf("%lld\n",ans); return 0; }
F .Clear the String
题意:给定长度为N的字符串,每次可以删去一段连续的相同字符,求最小删去次数。(N<500)
思路:区间DP,反过来就是“刷墙”这种题:每次可以刷一段,问最少刷的次数。 如果col[L]=col[R],我们可以先刷[L,R],然后去刷[L+1,R-1];如果中间有相同的,还可以分子区间。所以是个需要枚举中间点的O(N^3)区间DP。(比赛的时候想N^2做,WA了。
#include<bits/stdc++.h> #define ll long long #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=510; int dp[maxn][maxn]; char a[maxn]; int main() { int N,tot=1; scanf("%d%s",&N,a+1); rep(i,2,N){ if(a[i]!=a[i-1]) a[++tot]=a[i]; } N=tot; memset(dp,0x3f,sizeof(dp)); rep(len,1,N){ rep(L,1,N-len+1){ int R=L+len-1; if(len==1) dp[L][R]=1; else if(len==2) dp[L][R]=(a[L]==a[R]?1:2); else { dp[L][R]=len; if(a[L]==a[R]) dp[L][R]=min(dp[L+1][R],dp[L][R-1]); else{ dp[L][R]=min(dp[L+1][R],dp[L][R-1])+1; rep(k,L,R) dp[L][R]=min(dp[L][R],dp[L][k]+dp[k][R]-1); } } } } printf("%d\n",dp[1][N]); return 0; }