基础练习(1)
1.
首先很好想到得是Bob是不可能赢的,这很好考虑。因为alice总是能把下一步更优的结果留给自己,这时候就可以考虑什么情况下是平局了。
平局的情况肯定是无论alice怎样选接下来的局面都是平局。那只有头尾一样的或者接下来必选的两个字符是连在一起的。所以直接考虑去掉头尾相等的再去掉连在一起的两个。这样最后如果可以保证最后可以把政哥字符串去掉即可
void slove(){
cin>>s;
int i=0,j=s.length()-1;
while(i<j){
if(s[i]==s[j]){
i++;
j--;
}
else break;
}
while(i<j){
if(s[i]==s[i+1]) i+=2;
else if(s[j]==s[j-1]) j-=2;
else break;
}
if(i<j){
cout<<"Alice"<<endl;
}
else cout<<"Draw"<<endl;
}
2.
远古老题目。一开始想这不区间dp裸题。但是数据范围太大。然后考虑对可以匹配的点都标记一下。这时再考虑是不是只要是一段连续的1即可。一个左括号和一个右括号相匹配,如果中间的括号都不匹配这是不可能的因为你中间总会存在左右括号。而这样的括号应该是会被匹配掉的。所以只要找连续的1即可。
void slove(){
cin>>s;
stack<int>st;
int len=s.length();
for(int i=0;i<len;i++){
if(s[i]=='(') st.push(i);
else {
if(!st.empty()){
vis[st.top()]=1;
vis[i]=1;
st.pop();
}
}
}
s+=" ";
int w=0,ans=0,tot=0;
for(int i=0;i<=len;i++){
if(vis[i]) w++;
else{
ans=max(ans,w);
w=0;
}
}
for(int i=0;i<=len;i++){
if(vis[i]) w++;
else{
if(w==ans) tot++;
w=0;
}
}
if(ans){
cout<<ans<<" "<<tot<<endl;
}
else cout<<0<<" "<<1<<endl;
}
3.
考虑每个贪心,每个数肯定是尽量去用最小约数去除掉。所以从小往大遍历i,如果碰到不能除掉的数就break。
void slove(){
cin>>n;
cin>>s;
s=" "+s;
int ans=0;
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=n;i++){
if(s[i]=='0'){
for(int j=i;j<=n;j+=i){
if(s[j]=='0'&&vis[j]==0){
//cout<<ans<<endl;
ans+=i;
vis[j]=1;
}
else{
if(s[j]=='1') break;
}
}
}
}
cout<<ans<<endl;
}
4.
严格鸽的思路。
考虑为了出去,肯定是要尽量多往一个方向走。考虑往一个方向狂走,如果走不动了,那我在考虑从往这个方向走到的最大值往另外一个方向走,这样往另外一个方向肯定也是会走更远的。最后如果走出来即可。
void slove(){
cin>>n>>k;
fel(i,1,n) cin>>a[i];
int suml,mxl,sumr,mxr;
suml=mxl=sumr=mxr=0;
int l=k-1,r=k+1;
while(l>=1&&r<=n){
int flag=1;
while(l>=1&&a[k]+mxr+suml+a[l]>=0){
flag=0;
suml+=a[l--];
mxl=max(mxl,suml);
}
while(r<=n&&a[k]+mxl+sumr+a[r]>=0){
flag=0;
sumr+=a[r++];
mxr=max(mxr,sumr);
}
if(flag) break;
}
if(l<1||r>n) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
5.
两种写法都值得写一些。
1.二分
可以考虑二分答案,然后枚举每个l可以对应的最右边的r。看是否可以在区间内0的个数满足条件,区间外1的个数也满足条件。
bool check(int x){
for(int l=1,r=0;l<=len;l++){
while(sum1[r]-sum1[l-1]<=x&&r<=len) r++;
r--;
if(sum2[l-1]+sum2[len]-sum2[r]<=x) return 1;
}
return 0;
}
void slove(){
cin>>s;
len=s.length();
s=" "+s;
for(int i=1;i<=len;i++){
sum1[i]=sum1[i-1]+(s[i]=='0');
sum2[i]=sum2[i-1]+(s[i]=='1');
}
int l=0,r=len,ans=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
}
2.滑动窗口
这个就稍微推了一个性质
就是最佳结果肯定是删除的1=剩下的0 剩下的数=剩0+剩1 =删1+剩1=tot(1) 所以就直接维护一个大小为tot(1)的窗口
void slove(){
cin>>s;
int n=s.size();
int cnt1=0;
for(auto i:s) cnt1+=(i=='1');
//cout<<cnt1<<endl;
int out1=cnt1,in0=0;
for(int i=0;i<cnt1;i++){
if(s[i]=='0') in0++;
else out1--;
}
int ans=max(in0,out1);
for(int i=cnt1;i<n;i++){
//前端
if(s[i]=='0') in0++;
else out1--;
if(s[i-cnt1]=='0') in0--;
else out1++;
ans=min(ans,max(in0,out1));
}
cout<<ans<<endl;
}
6.
二分时间看是否向左走的最大位置是否与向右走的最小位置重合。
考虑只可能出现在0.5的位置所以直接*2然后最后是奇数输出即可。
bool check(int p){
int L=0,R=2e10;
for(int i=1;i<=n;i++){
if(p>t[i]){
L=max(a[i]-(p-t[i]),L);
R=min(a[i]+(p-t[i]),R);
}
else {
L=max(a[i],L);
R=min(a[i],R);
}
}
return R>=L;
}
void slove(){
n=read();
fel(i,1,n) a[i]=read(),a[i]=a[i]*2;
fel(i,1,n) t[i]=read(),t[i]=t[i]*2;
int l=-1,r=2e9;
while(r-l>1)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
int p=r,L=0,R=2e9;
for(int i=1;i<=n;i++){
if(p>t[i]){
L=max(a[i]-(p-t[i]),L);
R=min(a[i]+(p-t[
