题意:
A(CodeForces - 1272F):两个括号字符串是s、t,制造一个最短的括号字符串并使得s和t是它的字串(s和t可以不连续)。
B(CodeForces - 1272D):最多删除数组中的一个元素,求操作后的最长单调增序列的长度。
C(CodeForces - 1272A):三个数,分别可以加一、减一或者不变。求三者操作后分别两两做差的绝对值之和的最小值。
D(CodeForces - 1272E):长度为n的数列ai,每个位置i可以跳到i-ai或i+ai的位置。要求i和经过任意次跳跃后的位置的奇偶性不同。
E(CodeForces - 1272C):求有限个字符在字符串s中的子串个数。
F(CodeForces - 1272B):在对原串进行删除和重排后,求最长的字符串,使得机器人经过字符串的要求移动后能回到原点。
补题:
A:CodeForces 1272 F. Two Bracket Sequences
dp[i][j][k]表示匹配到s的第i个字符,匹配到t的第j个字符,并且此时(的个数比)多k个的时候的最小合法序列长度,k的上限是200(s和t中最多200个(或者))。
状态转移:
枚举答案合法序列的每一位放置(或者)
放置(,如果s[i]== ‘(’ -> ni=i+1,t[j]==’(’ -> nj=j+1, dp[ni][nj][z+1]=dp[i][j][z]+1
放置),如果s[i]== ‘)’ -> ni=i+1,t[j]==’)’ -> nj=j+1, dp[ni][nj][z-1]=dp[i][j][z]+1
整个过程需要满足0<=z<=200 下界是因为z<0时左括号个数小于右括号个数将无法形成合法序列。每一个步转移需要记录父节点坐标和父节点通过什么字符转移到当前状态,最终状态为dp[s.size()][t.size()][0],从这个状态沿着父节点回退到dp[0][0][0]。
代码:
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=210;
const int inf=0x3f3f3f3f;
int dp[maxn][maxn][maxn];
struct node{int x,y,z;char c;}st[maxn][maxn][maxn];
string s,t;
int sz,tz;
int nx,ny,nz;
inline void bfs(){
sz=s.size(),tz=t.size();
memset(dp,0x3f,sizeof dp);
dp[0][0][0]=0;
queue<node> q;q.push(node{0,0,0});
while(!q.empty()){
node tp=q.front();q.pop();
//'('
nx=tp.x+(tp.x<sz&&s[tp.x]=='(');
ny=tp.y+(tp.y<tz&&t[tp.y]=='(');
nz=tp.z+1;
if(nz<=200&&dp[nx][ny][nz]==inf){
dp[nx][ny][nz]=dp[tp.x][tp.y][tp.z]+1;
q.push(node{nx,ny,nz});
st[nx][ny][nz]=node{tp.x,tp.y,tp.z,'('};
}
//)
nx=tp.x+(tp.x<sz&&s[tp.x]==')');
ny=tp.y+(tp.y<tz&&t[tp.y]==')');
nz=tp.z-1;
if(nz>=0&&dp[nx][ny][nz]==inf){
dp[nx][ny][nz]=dp[tp.x][tp.y][tp.z]+1;
q.push(node{nx,ny,nz});
st[nx][ny][nz]=node{tp.x,tp.y,tp.z,')'};
}
}
}
int main(){
cin>>s>>t;
bfs();
string res="";
int x=sz,y=tz,z=0;
int px,py,pz;
while(x||y||z){
res+=st[x][y][z].c;
px=st[x][y][z].x;
py=st[x][y][z].y;
pz=st[x][y][z].z;
x=px,y=py,z=pz;
}
sz=res.size();
for(int i=sz-1;i>=0;--i)cout<<res[i];
cout<<endl;
return 0;
}
B:CodeForces 1272D Yet Remove One Element(AC)(暴力转dp)
思路:一开始想着三重循环,i表示删除的位置,j表示起始位置,k表示从j开始找递增子列。考虑到超时的可能性,在第一次错了之后,还是决定变换整体思路,现在想想,可能是边界的问题。之后想到用两个dp数组来记录第i个的递增个数和记录最大情况。但提交的时候应该是2个dp数组赋初始值的是时候有点差异(一个是先在外面赋值1,另一个是在里面判断在赋值),test5过不去,有点懵…
代码:
#include <iostream>
using namespace std;
int x[200010],y[200010],a[200010];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
x[1]=1;
for(int i=2;i<n;i++)
{
if(a[i]>a[i-1]) x[i]=x[i-1]+1;
else
x[i]=1;
}
y[n]=1;
for(int i=n-1;i>1;i--)
{
if(a[i+1]>a[i]) y[i]=y[i+1]+1;
else
y[i]=1;
}
int ans=1;
if(a[1]<a[2]) ans=x[1]+y[2];
for(int i=3;i<=n;i++)
{
if(a[i]>a[i-1]) ans=max(ans,x[i-1]+y[i]);
if(a[i]>a[i-2]) ans=max(ans,x[i-2]+y[i]);
}
cout<<ans<<endl;
return 0;
}
C:CodeForces - 1272A Three Friends(AC)(暴力)
思路:一开始就想用三个数的平均数的上取整来比较判断,没过。然后就三重循环暴力求了(ans最大值的取值一开始取小了,模拟的时候贪了,没模拟到测试里的最大值,导致没看出来)。
代码:
`#include<bits/stdc++.h>
#define MAX 3000000010
using namespace std;
int q,a,b,c;
long long ans,sum;
int main ()
{
cin>>q;
while(q--)
{
ans=2000000000;
cin>>a>>b>>c;
a=a-2;
b=b-2;
c=c-2;
for(int i=0; i<3; i++)
{
a++;
for(int j=0; j<3; j++)
{
b++;
for(int k=0; k<3; k++)
{
c++;
sum=abs(a-b)+abs(a-c)+abs(b-c);
ans=min(sum,ans);
}
c=c-3;
}
b=b-3;
}
cout<<ans<<endl;
}
}`
D:CodeForces - 1272E Nearest Opposite Parity
思路:反向建边,直接将所有的奇性点入队,然后bfs,依次遍历到的偶性点 的 步数,即为结果。再同样跑一遍偶性点
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
const int inf=0x3f3f3f3f;
int arr[maxn];
int ans[maxn];
vector<int>edge[maxn];
int n;
void solve(int flag)
{
int temp[maxn];
bool vis[maxn];
memset(temp,0,sizeof temp);
memset(vis,false,sizeof vis);
queue<int>q;
for(int i=1;i<=n;++i)
{
if((arr[i]&1)==flag)
{
q.push(i);
//cout<<"??"<<i<<endl;
vis[i]=true;
}
}
while(!q.empty())
{
int f=q.front();
q.pop();
//cout<<"pls"<<f<<endl;
for(int i=0;i<edge[f].size();++i)
{
if(!vis[edge[f][i]])
{
q.push(edge[f][i]);
vis[edge[f][i]]=true;
temp[edge[f][i]]=temp[f]+1;
}
}
}
for(int i=1;i<=n;++i)
{
if(vis[i]&&!((arr[i]&1)==flag))
ans[i]=temp[i];
}
}
int main()
{
cin>>n;
for(int i=1; i<=n; ++i)
cin>>arr[i];
for(int i=1;i<=n;++i)
{
if(i-arr[i]>=1)
{
edge[i-arr[i]].push_back(i);
}
if(i+arr[i]<=n)
{
edge[i+arr[i]].push_back(i);
}
}
solve(1);
solve(0);
for(int i=1;i<=n;++i)
{
if(ans[i])
cout<<ans[i]<<" ";
else
cout<<-1<<" ";
}
return 0;
}
E:CodeForces - 1272C Yet Another Broken Keyboard
思路:先找一个符合条件的最长连续字串,再用等比数列求和公式,注意数据类型用long long。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,k,sum,f,ans,b[27]={0};
string s;
char a[27],x;
int main ()
{
cin>>n>>k;
cin>>s;
sum=0;
ans=0;
for(int i=0; i<k; i++)
{
cin>>a[i];
b[a[i]-'a'+1]=1;
}
for(int i=0; i<n; i++)
{
if(b[s[i]-'a'+1])
sum++;
else{
ans=ans+sum*(sum+1)/2;
sum=0;
}
}
ans=ans+sum*(sum+1)/2;
cout<<ans<<endl;
}
F:CodeForces - 1272B Snow Walking Robot
思路:想要回原点,那L和R数量需要相等,且U和D数量需要相等。分两种情况考虑:如果竖直方向或水平方向不可以移动,那只能移一次水平或者竖直方向;否则移个正方形即可
代码:
#include<bits/stdc++.h>
#include<string>
using namespace std;
int main ()
{
int q,le;
cin>>q;
while(q--)
{
string s;
cin>>s;
int u=0,d=0,l=0,r=0;
le=s.length();
//cout<<le<<endl;
for(int i=0; i<le; i++)
{
if(s[i]=='U')
u++;
else if(s[i]=='D')d++;
else if(s[i]=='L')l++;
else if(s[i]=='R')r++;
}
int x=min(l,r),y=min(u,d);
if(x==0)
{
if(y==0)
cout<<0<<endl;
else
cout<<2<<endl<<"UD"<<endl;
}
else
if(y==0){
if(x==0)
cout<<0<<endl;
else
cout<<2<<'\n'<<"LR\n";
}
else
{
cout<<(x+y)*2<<endl;
string ans="";
for(int i=0;i<x;i++)
ans=ans+"L";
for(int i=0;i<y;i++)
ans=ans+"U";
for(int i=0;i<x;i++)
ans=ans+"R";
for(int i=0;i<y;i++)
ans=ans+"D";
cout<<ans<<endl;
}
}
}
感想:在早上的比赛后,过了BC两题,尝试过一题。尝试的E题感觉把自己骗了,只考虑了暴力的算法在tel之后没有继续去考虑优化的方法,转而去做别的题,有点可惜,毕竟事后发现挺简单的。C题主要是细节的问题没出来好,小错误导致白白wa了两次。没有全部看一遍的原因应该还是想慢了,在BC题的优化还是花太多时间了,而且现在看了AD的题解后,确实感觉自己的能力有点不足,还要继续补充专业的一些知识点。