abc386
__Inv_day_in_R__ · · 题解
A题
暴力统计每个数出现次数。
B题
枚举每一位,如果是零,找到后面有连续多少个,除以二向上取整。
C题
从左往右枚举一样的前缀,从右往左枚举一样的后缀,统计两个字符串除去前后缀还剩多少个字符,如果都小于等于1,可以一次操作完成。注意枚举后缀时不要与前缀重合。
void solve(){
int k;
string s,t;
cin>>k>>s>>t;
int sl=0,sr=s.size()-1,tl=0,tr=t.size()-1;
while(sl<s.size()&&tl<t.size()&&s[sl]==t[tl])sl++,tl++;
while(sr>=sl&&tr>=tl&&s[sr]==t[tr])sr--,tr--;
int lens=sr-sl+1,lent=tr-tl+1;
if(max(lens,lent)<=1)cout<<"Yes";
else cout<<"No";
}
D题
每个点按行升序排列,从上往下扫,维护到现在为止白色点所在列最靠左的位置,从这个位置往右必须填白色,所以如果枚举到有黑色点在右边,显然无解。
void solve(){
int n,m;
cin>>n>>m;
vector<tuple<int,int,char>>v(m);
for(auto&[x,y,c]:v)cin>>x>>y>>c;
sort(all(v));
int mn=n+1;
for(auto[x,y,c]:v){
if(c=='W')mn=min(mn,y);
else if(mn<=y){
cout<<"No";
return;
}
}
cout<<"Yes";
}
也可以求出所有必须填黑色的点,判断是否有重合部分。
E题
看到题面就是暴力DFS,在DFS时,有几个小优化,可以大大降低时间复杂度。
- 如果剩下的数字不够选,直接退出。
- 在DFS时没有必要枚举后面每一个可选的数,像01背包一样只枚举当前数字选或不选
-
具体实现见代码。
int n,k,ans=0;
vector<int>v;
void dfs(int x,int xr,int cnt){
if(k==cnt){ans=max(ans,xr);return;}
if(n-x<k-cnt)return;
dfs(x+1,xr^v[x],cnt+1);
dfs(x+1,xr,cnt);
}
void solve(){
cin>>n>>k;
v.resize(n);
cin>>v;
if(k*2>n){
int all=0;
for(auto&i:v)all^=i;
k=n-k;
dfs(0,all,0);
}
else dfs(0,0,0);
cout<<ans;
}
F题
DP。看C题的时候就想到编辑距离这题,设计DP状态:
但是有一些不同,如数据范围
记得特判
代码:
int k;
string s,t;
int f[500010][45];
int&dp(int i,int j){return f[i][i-j+22];}//通过传引用的方式实现dp与f的转化
void solve(){
cin>>k>>s>>t;
int n=s.size(),m=t.size();
if(abs(n-m)>k){
cout<<"No";
return;
}
memset(f,0x3f,sizeof(f));
for(int i=0;i<=k;i++){
dp(0,i)=i;
dp(i,0)=i;
}
for(int i=1;i<=n;i++)for(int j=max(1ll,i-k);j<=min(i+k,m);j++){
dp(i,j)=min({dp(i-1,j)+1,dp(i,j-1)+1,dp(i-1,j-1)+(s[i-1]!=t[j-1])});
}
if(dp(n,m)<=k)cout<<"Yes";
else cout<<"No";
}