B题:
给一个又A和B组成的字符串
使用魔法选择一个字典序不递增的子串, 然后使得这个子串变成字典序不递减的子串
求使用至多依次魔法后,这个字符串能够出现的最长的字典序不递减的子串的长度为多少。
思路:
将连续的A和连续的B统计其数量,然后答案即为连续三个或四个A和B的数量和
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=2e5+5; int n,m; int a[200005]; int main(){ int n; string s; cin>>n; cin>>s; vector< pair<int,int> > v; bool flag=1; int cnt=0;//1代表A for(int i=0;i<n;i++){ if(('A'==s[i])==flag){ cnt++; }else{ v.push_back({flag,cnt}); cnt=1; flag^=1; } } v.push_back({flag,cnt}); v.push_back({0,0}); v.push_back({0,0}); v.push_back({0,0}); int ans=v[0].second; for(int i=0;i<v.size()-3;i++){ if(v[i].first==1) ans=max(ans,v[i].second+v[i+1].second+v[i+2].second+v[i+3].second); else ans=max(ans,v[i].second+v[i+1].second+v[i+2].second); } cout<<ans<<endl; }
C题:
白浅获得了一个仅由A和B组成的字符串。他可以至多使用一次魔法来改变字符串。
魔法:选择一个子串,满足子串中 A 的数量等于 B 的数量,然后按字典序从小到大排序这个子串,即变成形如AAA...AAABBB...BBB这样的字符串(A和B的数量均与原来的子串相同)。
他想知道,在他至多使用一次魔法后,这个字符串能够出现的最长的字典序不递减的子串的长度为多少。
思路:
统计每个数前面有多少个连续的A,后面有多少个连续的B。
然后统计字符串的前缀和,前缀和相同说明这段区间A和B个数是相等的
再加上左边的A和右边的B就是这段区间修改后的可以达到的最大长度
代码:
#include<bits/stdc++.h> using namespace std; int n,ans=0; char ch; char a[200005]; int preA[200005],sufB[200005]; map<int,int>mp; int main(){ cin>>n; //n=2e5 for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ if(a[i]=='A') preA[i]=preA[i-1]+1; ans=max(ans,preA[i]); } for(int i=n;i>=1;i--){ if(a[i]=='B') sufB[i]=sufB[i+1]+1; ans=max(sufB[i],ans); } int dif=0; for(int i=1;i<=n;i++){ if(a[i]=='A') dif++; else dif--; if(mp[dif]==0) mp[dif]=i; else{ ans=max(ans,i-mp[dif]+preA[mp[dif]]+sufB[i+1]); } } cout<<ans<<endl; }
I题:
n×m的图中,左上角为(0,0),右下角为(n-1,m-1),0代表空地,1代表障碍
求从(sx,sy)到(ex,ey)最少拐弯次数
思路:
bfs,选择方向优先选择和上次同向,同一端点可以访问多次,vis小于一个定值即可
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=2e5+5; int gcd(int a,int b){ return a%b?gcd(b,a%b):b; } bool a[5005][5005]; int n,m,sx,sy,ex,ey; int ans; bool flag=0; int dir[4][2]={1,0,-1,0,0,1,0,-1}; int vis[5005][5005]; struct node{ int x,y,last,num; }; void bfs(){ ans=1000000; memset(vis,0,sizeof(vis)); queue<node> q; q.push({sx,sy,-1,0}); while(!q.empty()){ node cnt=q.front(); q.pop(); if(cnt.x==ex&&cnt.y==ey){ ans=min(ans,cnt.num); continue; } if(cnt.num>=ans)continue; if(cnt.last==-1){ for(int i=0;i<4;i++){ int xx=cnt.x+dir[i][0],yy=cnt.y+dir[i][1]; if(xx>=0&&yy>=0&&xx<n&&yy<n&&a[xx][yy]==0){ vis[xx][yy]++; q.push({xx,yy,i,0}); } } }else{ for(int i=cnt.last;i<cnt.last+4;i++){ int xx=cnt.x+dir[i%4][0],yy=cnt.y+dir[i%4][1]; if(xx>=0&&yy>=0&&xx<n&&yy<n&&a[xx][yy]==0&&vis[xx][yy]<5){ vis[xx][yy]++; q.push({xx,yy,i%4,cnt.num+(i==cnt.last?0:1)}); } } } } } int main(){ cin>>n>>m>>sx>>sy>>ex>>ey; string s; for(int i=0;i<n;i++){ cin>>s; for(int j=0;j<m;j++){ a[i][j]=s[j]-'0'; } } bfs(); if(ans==1000000) cout<<-1<<endl; else cout<<ans<<endl; }
F题:
给一个n、m
有n层,每层有一个x[i],x[i]代表这层有多少个宝物,每个宝物有一个价值
总共可以取m个宝物
每层只能从两边开始取
求取得的宝物的最大价值和
思路:
计算每层取i个时的最大值
背包dp,状态转移方程dp2[i][k]=max(dp2[i][k],dp2[i-1][k-j]+dp[i][j]);
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=2e5+5; int n,m; int x[105],c[105][105],dp[105][105],pre[105][105],dp2[105][10005]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ cin>>x[i]; for(int j=1;j<=x[i];j++){ cin>>c[i][j]; pre[i][j]=pre[i][j-1]+c[i][j]; } for(int j=0;j<=x[i];j++){//枚举取多少个 for(int k=0;k<=j;k++){//枚举上楼梯时取多少个 dp[i][j]=max(dp[i][j],pre[i][k]+pre[i][x[i]]-pre[i][x[i]-(j-k)]); } for(int k=m;k>=j;k--){ dp2[i][k]=max(dp2[i][k],dp2[i-1][k-j]+dp[i][j]); } } } cout<<dp2[n][m]<<endl; }