网址: https://vjudge.net/contest/381166
codeforce的比赛网址: https://codeforces.com/contest/1272
A(div.3 F)
题意:就是输入两个a,b字符串,然后找到一个最短的c字符串,其中组成由a,b的子串。
这一题是第一题我也看没想好怎么做,哈哈哈,然后现在我看了比赛的代码也不是太懂,然后就又找了网上的代码。
网上代码:
分析:设 dp[i][j][z] 表示已经 匹配到 序列 a 的第 i 位,序列 b 的第 j 位,当前左括号比右括号多 z 个的最短长度(规则序列,从左往右左括号当然只能比右括号多),则最终答案是 dp[n][m][0] ( n=len(a) , m=len(b) ) – 转移状态:通过枚举左右括号
①: ( – 则 z+1,第 i , j 位若是 ‘(’ 加一,否则不加,dp[ii][jj][z+1]=dp[i][j][z]+1;
②: ) – 则 z -1,第 i , j 位若是 ‘)’ 加一,否则不加,dp[ii][jj][z- 1]=dp[i][j][z]+1;
注意要判断 z 的边界,因为要记录路径,所以,开个结构体同步更新就可以了。
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
const int N = 200+5;
const int INF = 0X3F3F3F3F;
char a[N],b[N];
int n,m;
int dp[N][N][N];
vector<char>ans;
struct node
{
int x,y,z; char c;
node(int _x=0,int _y=0,int _z=0,char _c=0)
{
x=_x,y=_y,z=_z,c=_c;
}
}p[N][N][N];
void bfs()
{
memset(dp,INF,sizeof(dp));
queue<node>que;
que.push(node(0,0,0));
dp[0][0][0]=0;
while(!que.empty())
{
node top=que.front(); que.pop();
int nx=top.x+(top.x<n&&a[top.x]=='(');
int ny=top.y+(top.y<m&&b[top.y]=='(');
int nz=top.z+1;
if(nz<=200&&nz>=0&&dp[nx][ny][nz]==INF)
{
dp[nx][ny][nz]=dp[top.x][top.y][top.z]+1;
p[nx][ny][nz]=node(top.x,top.y,top.z,'(');
que.push(node(nx,ny,nz));
}
nx=top.x+(top.x<n&&a[top.x]==')');
ny=top.y+(top.y<m&&b[top.y]==')');
nz=top.z-1;
if(nz<=200&&nz>=0&&dp[nx][ny][nz]==INF)
{
dp[nx][ny][nz]=dp[top.x][top.y][top.z]+1;
p[nx][ny][nz]=node(top.x,top.y,top.z,')');
que.push(node(nx,ny,nz));
}
}
}
int main()
{
scanf("%s%s",a,b);
n=strlen(a);
m=strlen(b);
bfs();
node k=p[n][m][0];
while(k.x>0||k.y>0||k.z>0)
{
ans.push_back(k.c);
k=p[k.x][k.y][k.z];
}
ans.push_back(k.c);
reverse(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++) printf("%c",ans[i]);
puts("");
}
比赛中的代码:
#include<bits/stdc++.h>
using namespace std;
const int mxn=203,inf=mxn<<1;
char a[mxn],b[mxn];
int d[mxn][mxn][mxn],n,m,l;
int f[mxn][mxn][mxn];
void dg(int x,int y,int k){
if(x==0&&y==0&&k==0){
return;
}
if(f[x][y][k]==1){
dg(x,y,k-1);
putchar('(');
} else if(f[x][y][k]==2){
dg(x-1,y-1,k-1);
putchar('(');
} else if(f[x][y][k]==3){
dg(x-1,y,k-1);
putchar('(');
} else if(f[x][y][k]==4){
dg(x,y-1,k-1);
putchar('(');
} else if(f[x][y][k]==5){
dg(x,y,k+1);
putchar(')');
} else if(f[x][y][k]==6){
dg(x-1,y-1,k+1);
putchar(')');
} else if(f[x][y][k]==7){
dg(x-1,y,k+1);
putchar(')');
} else if(f[x][y][k]==8){
dg(x,y-1,k+1);
putchar(')');
} else printf("??????\n");
}
int main(){
scanf("%s%s",a+1,b+1);
n=strlen(a+1);
m=strlen(b+1);
l=max(m,n);
for(int i=0;i<mxn;i++) for(int j=0;j<mxn;j++) for(int k=0;k<mxn;k++) d[i][j][k]=inf;
for(int i=0;i<=l;i++){
d[0][0][i]=i;
f[0][0][i]=1;
}
for(int i=0;i<=n;i++) for(int j=0;j<=m;j++){
if(i==0&&j==0) continue;
for(int k=0;k<=l;k++){
if(k>0){
if(i&&j&&a[i]=='('&&b[j]=='('){
if(d[i-1][j-1][k-1]+1<d[i][j][k]){
d[i][j][k]=d[i-1][j-1][k-1]+1;
f[i][j][k]=2;
}
}
else if(i&&a[i]=='('){
if(d[i-1][j][k-1]+1<d[i][j][k]){
d[i][j][k]=d[i-1][j][k-1]+1;
f[i][j][k]=3;
}
}
else if(j&&b[j]=='('){
if(d[i][j-1][k-1]+1<d[i][j][k]){
d[i][j][k]=d[i][j-1][k-1]+1;
f[i][j][k]=4;
}
}
}
if(i&&j&&a[i]==')'&&b[j]==')'){
if(d[i-1][j-1][k+1]+1<d[i][j][k]){
d[i][j][k]=d[i-1][j-1][k+1]+1;
f[i][j][k]=6;
}
}
else if(i&&a[i]==')'){
if(d[i-1][j][k+1]+1<d[i][j][k]){
d[i][j][k]=d[i-1][j][k+1]+1;
f[i][j][k]=7;
}
}
else if(j&&b[j]==')'){
if(d[i][j-1][k+1]+1<d[i][j][k]){
d[i][j][k]=d[i][j-1][k+1]+1;
f[i][j][k]=8;
}
}
}
for(int k=1;k<=l;k++){
if(d[i][j][k-1]+1<d[i][j][k]){
d[i][j][k]=d[i][j][k-1]+1;
f[i][j][k]=1;
}
}
for(int k=l;k>=0;k--){
if(d[i][j][k+1]+1<d[i][j][k]){
d[i][j][k]=d[i][j][k+1]+1;
f[i][j][k]=5;
}
}
}
return 0:
}
B(div.3 D)
题意:就是删除一个数然后找到最长的单调递增的序列。
比赛中的最简代码:
#include <iostream>
#include <iomanip>
#define LL long long
using namespace std;
const int N=2e5+5;
int a[N];
int f[N],g[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
f[i]=g[i]=1;
for(int i=2;i<=n;i++)
{
if(a[i-1]<a[i]) f[i]=f[i-1]+1;
}
for(int i=n-1;i>0;i--)
{
if(a[i]<a[i+1]) g[i]=g[i+1]+1;
}
int ans=1;
for(int i=1;i<=n;i++)
{
if(a[i+1]>a[i-1]||i==n) ans=max(ans,f[i-1]+g[i+1]);
ans=max(ans,f[i]);
}
cout<<ans<<endl;
return 0;
}
我比赛的代码:
只是在最后的时候有一点不一样,但大致意思是一样的。
思路:
首先这个题目应该是找到最长的递增序列,所以要找到前面最长的和后面最长的然后if(a[i]<a[i+2])说明去掉i+1号位置后,以i号位置为终点的最长递增序列的最后一个数a[i]小于以i+2号位置为起点的最长递增序列的第一个数a[i+2],然后这两部分就可以连接起来就最长了。
for(int i=1;i<=n-2;i++)
{
if(a[i]<a[i+2]){
maxnn=max(maxnn,l[i]+r[i+2]);
}
}
C(div.3 A)
题意:大致意思就是三个数,可以分别对这三个数进行放大一个,或者放小一个,然后使两两分别进行取差的和到最小。
比赛中的最简形式:
#include <iostream>
using namespace std;
int main() {
int t;
cin>>t;
while (t--)
{
int a,b,c;
cin>>a>>b>>c;
cout<<max(0, abs(a-b)+abs(a-c)+abs(b-c)-4)<<endl;
}
}
思路:
在赛的时候我所得到的是(最大值的两倍-最小值的两倍-4)这个应该符合一部分的题目,但是还要参杂一定的条件,而且还要对他进行排序运算,而这个人的方法则是我的想法的简单版的感觉,但是我总是在绕圈子,而且根本不需要ll。
我的比赛时候的代码:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int main()
{
int q;
ll a[3],sum;
cin>>q;
while(q--)
{
for(int i=1;i<=3;i++)
{cin>>a[i];}
sort(a+1,a+4);
if(a[3]-a[2]<=1&&a[2]-a[1]<=1)
sum=0;
else
sum=2*a[3]-2*a[1]-4;
cout<<sum<<endl;}
return 0;}
D(div.3 E)
题意:就是一个长度为n的序列a,他可以动,在i位置的时候可以向i+ai和i-a[i]动,然后到达一个位置j,最后造成ai和aj奇偶性不同。
思路:没有,看了大佬的也是看太懂,然后又找到博客
https://blog.csdn.net/Miaplacidus/article/details/103589010?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2
#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int a[maxn],dist[maxn];
vector<int> G[maxn];
int main()
{
memset(dist,-1,sizeof(dist));
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
queue<int> QAQ;
for(int i=1;i<=n;i++)//反向建边
{
if(i+a[i]<=n)
G[i+a[i]].push_back(i);
if(i-a[i]>=1)
G[i-a[i]].push_back(i);
}
for(int i=1;i<=n;i++)//将所有能一步到位的预处理出来
{
int x=i+a[i];
if(x<=n&&(a[x]^a[i])&1)//奇偶性不同
dist[i]=1;
x=i-a[i];
if(x>=1&&(a[x]^a[i])&1)
dist[i]=1;
if(dist[i]==1)
QAQ.push(i);
}
while(!QAQ.empty())
{
int x=QAQ.front();
QAQ.pop();
for(auto &v:G[x])
if(dist[v]==-1)//这里没有能一步跳到奇偶性不同的点
{
dist[v]=dist[x]+1;
QAQ.push(v);
}
}
for(int i=1;i<=n;i++)
{
cout<<dist[i]<<' ';
}
return 0;
}
E(div.3 C)
题意:一个字符串s,仅由小写英文字母构成。他想打出所有连续非空子串。可是,他发现他的键盘坏了,只能打出26字母中的k个。请求出用这个坏掉的键盘,最多能打出多少个字符串s的连续非空子串。
思路:这个就是但是看懂了啥意思,但是呢不知道怎么去把他表示出来。然后看了两个人的代码,明白了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[200005];
bool mp[30];
ll dp[200005];
int main()
{
ll n,k;
cin>>n>>k>>s+1;
for(int i=0;i<k;i++)
{
char ch;
cin>>ch;
mp[ch-'a']=1;//先用mp记录下输入的字符串
}
ll ans=0;
for(int i=1;i<=n;i++)
{
if(!mp[s[i]-'a'])
dp[i]=i;
else
dp[i]=dp[i-1];
ans+=dp[i];
}
cout<<n*(n+1)/2-ans<<endl;
return 0;
}
#include <iostream>
using namespace std;
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
bool arr[26];
using ll = long long;
int main()
{
SIS;
int chong;
char ch;
int n,k;
cin >> n >> k;
string s;
cin >> s;
for(int i=0;i<k;i++)
{
cin >> ch;
arr[ch-'a']=true;
}
ll ans=0,cnt=0;
for(int i=0;i<n;i++)
{
if(arr[s[i]-'a']) cnt++;
else cnt=0;
ans+=cnt;
}
cout << ans << endl;
return 0;
}
F(div.3 B)
题意:就是走一个格子一个格子,可以像左像右或者向前向后一格,
对刚开始的步伐进行删减,最后走到(0,0)。
思路:这个题目就是先前向后一样多,然后向左向右一样的。所以就先计算一下那个多,那个少,就进行多填后者少补的计划就可以了。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
ll t,n,r,i,j,k,sum,ans,cnt,a,b,m,c,d;
const int maxn=100005;
char str[maxn],str1[maxn];
int main()
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>str;
int len=strlen(str);
a=0;
b=0;
c=0;
d=0;
for(int i=0;i<len;i++)
{
if(str[i]=='L')
a++;
if(str[i]=='R')
b++;
if(str[i]=='U')
c++;
if(str[i]=='D')
d++;
}
ans=min(a,b);
cnt=min(c,d);
sum=2*ans+2*cnt;
if(ans&&cnt)
{
cout<<sum<<endl;
for(i=1;i<=ans;i++) cout<<"L";
for(i=1;i<=cnt;i++) cout<<"U";
for(i=1;i<=ans;i++) cout<<"R";
for(i=1;i<=cnt;i++) cout<<"D";
cout<<endl;
}
else
{
if(ans) cout<<"2"<<endl<<"LR"<<endl;
else if(cnt) cout<<"2"<<endl<<"UD"<<endl;
else cout<<0<<endl;
}
}
}
每日小结:
今天的比赛做了两道题目在规定的时间,题目都过了一遍,但是有的只是单纯的看懂题目的要求而已,但是不知道如何去实现,在这次的补提中发现又很多的dp的元素,但是我这一部分好像学的还是不是太好,还要再巩固巩固,还有就是对于简单的题目可以尽快的做出和简单不要绕弯路了。
🆗✔,最后祝福明天高考的同学们做到都对,蒙的也都对,做个好梦,加油!💪