A
题目分析:
记录隔得最远的两个'B'的距离
C++代码:
#include<iostream>
using namespace std;
int main(){int t;cin>>t;while(t--){int n;cin>>n;string s;cin>>s;int a=0,b=0,cnt=0;//a:第一个B的下标 b:最后一个B的下标for(int i=0;i<s.size();i++)if(s[i]=='B'){cnt++;if(cnt==1)a=i;b=max(b,i);}cout<<b-a+1<<endl;}return 0;
}
B
题目分析:
从前往后填,每次枚举26个英文字母的使用次数,与a[i]相等就用该字母,用完次数记得加一
C++代码:
#include<iostream>
using namespace std;
const int N=200010;
int a[N];
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){int n,cnt[26]={0};cin>>n;string s;for(int i=0;i<n;i++)cin>>a[i];for(int i=0;i<n;i++){for(int j=0;j<26;j++)if(cnt[j]==a[i]){//如果字母在此之前使用的次数为a[i]次,则用该字母s+=(char)(j+'a');cnt[j]++;//该字母的使用次数加一break;}}cout<<s<<endl;}return 0;
}
C
题目分析:
找到所有1~k的元素中
第一个数组中出现的元素个数sum1(去重后)
第二个数组中出现的元素个数sum2(去重后)
二者的公共元素sum3(去重后)
sum1-=sum,sum2-=sum,此时sum1和sum2就是两个数组独有的元素个数
1、sum1+sum+sum2<k 一定找不到可行方案
2、sum1+sum+sum2==k
如果sum1<=k/2&&sum2<=k/2则表示一定可以找到方案
否则找不到
C++代码:
#include<iostream>
using namespace std;
const int N=400010;
int main(){int t;cin>>t;while(t--){int n,m,k,sum=0,sum1=0,sum2=0;//sum记录两个数组的公共元素(1~k中),sum1(sum2)记录第一(二)个数组中的元素个数(1~k中)cin>>n>>m>>k;int cnt1[N]={0},cnt2[N]={0};//记录两个数组中数出现的个数,方便判重 for(int i=0;i<n;i++){int x;cin>>x;if(x>=1&&x<=k){cnt1[x]++;if(cnt1[x]==1)sum1++;}}for(int i=0;i<m;i++){int x;cin>>x;if(x>=1&&x<=k){cnt2[x]++;if(cnt2[x]==1){if(cnt1[x])sum++;sum2++;}}}//sum1和sum2都减去公共元素sum1-=sum,sum2-=sum;//如果总元素个数不等于k,直接输出Noif(sum1+sum+sum2<k)puts("No");else{//否则一定等于kif(sum1<=k/2&&sum2<=k/2)puts("Yes");else puts("No");}}return 0;
}
D
题目分析:
用一个数组last[]记录每个元素前面与它最近的不相同的元素下标,从前往后更新last即可
C++代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=200010;
int a[N],last[N];
int n,q;
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){cin>>n;//last[i]记录上一个与a[i]不同的元素的下标memset(last,-1,sizeof last);for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=n;i++){if(a[i-1]!=a[i])last[i]=i-1;else last[i]=last[i-1];}cin>>q;while(q--){int l,r;cin>>l>>r;//如果与a[r]不同的上一个元素的下标小于l,则一定无解if(last[r]<l)cout<<-1<<" "<<-1<<endl;else cout<<last[r]<<" "<<r<<endl;//否则直接输出}}return 0;
}
E
题目分析:
max(s)-min(s)<=1
由于是排列,所以任意相邻的两个长度为k的子数组的和一定不同,所以需要构造成
s[i]-s[i-1]=1,s[i+1]-s[i]=-1...这样,才可以保证max(s)-min(s)<=1
举个例子:n=9,k=3
假设前三个元素为[x,y,z],由于s[2]-s[1]=第四个元素-x=1,所以第四个元素为x+1,同理构造如下:
[x,y,z,x+1,y-1,z+1,x+2,y-2,z+2]
所以把9分成[1,2,3]、[4,5,6]、[7,8,9]三块,奇数块从小到大输出,偶数块从大到小输出,所以
序列为1,6,7,2,5,8,3,4,9,由此可见,每个块中的元素每隔k次出现一次
1、n%k==0的情况,只需要分成k个块,按如上操作即可
2、n%k!=0,其实还是分成k个块,只是可能每个块中数的个数可能不一样
在代码中,我们并不会把分块的过程写出来,而是让第一个奇数块从l=1开始放,第一个偶数块从r=n开始放,每个奇数块放完就 l++,每个偶数块放完就 r--,然后下一个奇数块又从l开始放,偶数块又从r开始放,直到放满n个数
(看不太明白可以看代码模拟一下hh)
C++代码:
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=2e5+10;
int a[N];
void solve(){int n,k;cin>>n>>k;vector<int> ans(n+1);int l=1,r=n;for(int i=1;i<=k;i++){if(i&1){//奇数块从小到大输出for(int j=i;j<=n;j+=k){//每隔k个数放一个该块中的数ans[j]=l;l++;}}else{//偶数块从大到小输出for(int j=i;j<=n;j+=k){//每隔k个数放一个该块中的数ans[j]=r;r--;}}}for(int i=1;i<=n;i++)cout<<ans[i]<<" ";cout<<endl;
}int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int t;cin>>t;while(t--){solve();}return 0;
}
F
题意分析:
一开始看到这题感觉是tarjan(学tarjan学魔怔了hh)
然后发现并查集好像可以做,要求所有环内的最小边,故先将所有边存起来,然后从大到小排序
枚举每条边,如果两个端点不在一个连通块,则让这两个端点合并,否则加上这条边一定会形成环,就不加上该边,设置一个变量 t 存储每个环的最小边的编号
为什么是一个变量存储每个环 ?
因为我们是从大到小枚举每条边,每次枚举到一个环的时候,当前环的最小边一定比之前小,所以直接更新 t 即可
找到 t 后,用bfs搜出环内的所有点存起来然后输出即可
C++代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=200010;
int a[N],p[N];
bool st[N];
int pre[N];
int n,m;
struct Node{int u,v,w;
}e[N];//存储所有的边
vector<int> g[N];
int find(int x){//并查集求祖宗节点+路径压缩if(p[x]!=x)p[x]=find(p[x]);return p[x];
}
bool merge(int a,int b){int pa=find(a),pb=find(b);//如果a,b本来就在一个连通块,则表明加上这条边一定会形成环,返回false if(pa==pb)return false;//合并成功并返回true p[pa]=pb;return true;
}
bool cmp(Node a,Node b){//排序用return a.w>b.w;
}
void solve(){cin>>n>>m;for(int i=1;i<=n;i++)p[i]=i,g[i].clear();for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;e[i]={u,v,w};g[u].push_back(v),g[v].push_back(u);}sort(e+1,e+m+1,cmp);//将所有边从大到小排序 int t=0;//记录简单循环的最小边的编号 for(int i=1;i<=m;i++)if(!merge(e[i].u,e[i].v))//如果会成环,则不合并这两个点,且更新t t=i;int u=e[t].u,v=e[t].v;//记录权重最小的简单循环中的两个点的编号 memset(st,false,sizeof st);//宽搜出所有环内的点queue<int> q;q.push(u);st[u]=true;while(!q.empty()){int a=q.front();q.pop();for(int i=0;i<g[a].size();i++){//枚举a的所有的邻点 int b=g[a][i];if(a==u&&b==v)continue;if(st[b])continue;st[b]=true;pre[b]=a;//b是从a过来的 q.push(b);}}int cur=v;//先记录终点 vector<int> ans;while(cur!=u){ans.push_back(cur);//记录答案 cur=pre[cur];//往回搜 }ans.push_back(u);//不要忘记把起点加入到ans cout<<e[t].w<<" "<<(int)ans.size()<<endl;//输出最小边的权值和该简单循环中含有的点数 //输出答案 for(int i=0;i<ans.size();i++)cout<<ans[i]<<" ";cout<<endl;
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){solve();}return 0;
}
G
题意分析:
动态规划
这题的状态定义就很逆天,根本不是我这种蒟蒻能想到的 T^T
f[i][j][k]
1、状态表示:
集合:当前使用第i个元素,最左侧未被染色的位置是j,最右侧被染色的位置是k的所有 方案(即1~j-1全都被染色了,k+1~n没被染色,中间随意)
属性:Min(最小操作数)
2、状态计算:
a. 第i个元素不做任何操作 f[i][j][k]=f[i-1][j][k]
b. 向左染色 l=max(1,i-a[i]+1),染 [l,i] 这个区间
l>j时,此时最左边未被染色的位置还是j
①k>=i,最左边未被染色的位置还是j,最右侧被染色的位置还是k,忽略当前情况,不 用操作
②k<i,若染色,则f[i][j][max(i,k)]=min(f[i][j][max(i,k)],f[i-1][j][k]+1),但是此时[j,l-1]还 是有一部分未被染色的。由于我们最终要染完所有的位置,当前我们已经用到了第i 个数,所以后面[i+1,n]中一定有一个位置向前染色会覆盖 j ,故此时染[l,i]的区间并不 是最优的,当前情况也可忽略,不用操作
l<=j时
①k>i,最左边未被染色的位置是 k+1,最右边被染色的位置是k
为什么呢?因为k一定是i-1及其之前的位置向右染色染到了k,所以包含了i,此时就 [1,k]都被染色了。所以转移方程为:
f[i][k+1][k]=min(f[i][k+1][k],f[i-1][j][k]+1)
②k<=i,最左边未被染色的位置是i+1,最右边被染色的位置是 i
f[i][i+1][i]=min(f[i][i+1][i],f[i-1][j][k]+1)
int t=max(i,k)
状态转移方程:f[i][t+1][t]=min(f[i][t+1][t],f[i-1][j][k]+1)
c. 向右染色 r=min(i+a[i]-1,n),染 [i,r] 这个区间
①j<i,最左边未被染色的位置还是 j,最右边染色的位置是max(r,k)
转移方程:f[i][j][max(k,r)]=min(f[i][j][max(k,r)],f[i-1][j][k]+1)
这种情况跟b中②同样都会有后一个向前染色覆盖 j,但是这种情况不一定会覆盖 i 向 右染色的区间,所以这种情况是有意义的,要加上
②j>=i ,最左边未被染色的位置是max(r,k)+1,最右边染色的位置是max(r,k)
int t=max(i,k)
状态转移方程:f[i][t+1][t]=min(f[i][t+1][t],f[i-1][j][k]+1)
include<iostream>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
int a[N];
int f[N][N][N];
int n;
void solve(){cin>>n;for(int i=1;i<=n;i++)cin>>a[i];for(int i=0;i<=n;i++)for(int j=1;j<=n+1;j++)for(int k=0;k<=n;k++)f[i][j][k]=INF;f[0][1][0]=0;for(int i=1;i<=n;i++){for(int j=1;j<=n+1;j++){for(int k=0;k<=n;k++){f[i][j][k]=min(f[i][j][k],f[i-1][j][k]);int l=max(i-a[i]+1,1);if(l<=j){int t=max(i, k);f[i][t+1][t]=min(f[i][t+1][t],f[i-1][j][k]+1);}int r=min(i+a[i]-1,n);if(j<i){f[i][j][max(k,r)]=min(f[i][j][max(k,r)],f[i-1][j][k]+1);}else{int t=max(k, r);f[i][t+1][t]=min(f[i][t+1][t],f[i-1][j][k]+1);}}}}cout<<f[n][n+1][n]<<endl;
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t;cin>>t;while(t--){solve();}return 0;
}