题目链接:Dashboard - The 2021 ICPC Asia Shenyang Regional Contest - Codeforces
目录
E. Edward Gaming, the Champion
B. Bitwise Exclusive-OR Sequence
E. Edward Gaming, the Champion
题意:给你一个字符串,问该字符串中有多少个“edgnb”。
思路:签到题,直接遍历一遍字符串统计数目即可。
代码:
void solve() {
cin>>s;
int ans=0;
for(int i=0; i<(int)s.size()-4; i++) {
if(s[i]=='e'&&s[i+1]=='d'&&s[i+2]=='g'&&s[i+3]=='n'&&s[i+4]=='b')ans++;
}
cout<<ans<<endl;
}
F. Encoded Strings I
题意:给你一个字符串s,字符串中只包含英文小写字母,每个小写字母的映射方式为:
设该小写字母最后一个出现位置为i,字符串长度范围为1~n,x为s[i]~s[n]中不同字符的个数,则小写字母被映射为字母表中的第x个数(比如x=1,则映射为 ‘a’;x=2,则映射为 ‘b’)。
例如:字符串“aacc”会被映射为“bbaa”,而字符串“aca”会被映射为“aba”,
求解目标为长度1~n的前缀中字典序最小的映射后的字符串。
思路: 因为映射看的是每一个字母最后一个出现的位置,所以我们从后往前遍历字符串,得出每个字母的映射字母,最后暴力枚举得出每个前缀字符串的映射字符串,sort取字典序最小即可。
代码:
void solve() {
map<char,char>mp;
vector<string>v;
int n;
string s;
cin>>n>>s;
for(int i=0; i<n; i++) {
map<char,char>mp;
int cnt=0;
string k;
for(int j=i; j>=0; j--) {
if(mp[s[j]])k+=mp[s[j]];
else mp[s[j]]='a'+cnt,k+=mp[s[j]],cnt++;
}
reverse(k.begin(),k.end());
v.push_back(k);
}
sort(v.begin(),v.end());
cout<<v.back();
}
J. Luggage Lock
题意:给出一个4位密码锁,每次操作能滑动一个或多个相邻的位置向上或向下移动一格,现在给出多次询问,每次询问给出一个起点和终点,询问起点到终点的最少操作数。
思路: 我们可以先根据起点和终点的位置先得到他们的相对位置,比如起点为“1234”,终点为“2345”,那么他们的相对起点为“0000”,相对终点是“1111”,也就是他们两相减。这样,问题就转化成了起点“0000”到任意终点的最小操作数,这个最小操作数我们可以通过bfs来预处理,每次bfs标记走过的终点,并记录最小步数即可。
代码:
map<string,int>mp;
void bfs() {
queue<pair<string,int>>q;
string s="0000";
q.push({s,0});
while(!q.empty()) {
string p=q.front().first;
int step=q.front().second;
q.pop();
for(int i=0; i<4; i++) {
for(int j=i; j<4; j++) {
string t=p,tt=p;
for(int k=i; k<=j; k++) {
if(t[k]=='9')t[k]='0';
else t[k]++;
if(tt[k]=='0')tt[k]='9';
else tt[k]--;
}
if(!mp[t]&&t!="0000")mp[t]=step+1,q.push({t,step+1});
if(!mp[tt]&&tt!="0000")mp[tt]=step+1,q.push({tt,step+1});
}
}
}
}
void solve() {
string kk,a,b;
cin>>a>>b;
for(int i=0; i<4; i++) kk+=(b[i]-a[i]+10)%10+'0';
cout<<mp[kk]<<endl;
}
B. Bitwise Exclusive-OR Sequence
题意:给你一个n,表示有n个点,给你m个a,b,w,表示点a的权值和点b的权值异或值为w,求有可能的点权中最小的点权之和。
思路:因为对于二进制来说,每个位数的异或是独立的,是互不影响的,并且若已知一个连通块中某个点权的某个二进制位值,那么其它点权的该二进制位值就都知道了,所以只需枚举各个连通块中随便的一个点权的二进制位即可,并且二进制是0的情况和二进制是1的情况是对称的,取min累加即可,具体实现见代码与注释。
代码:
vector<PII> e[maxn];
bool st[maxn];
int n,m,ans=0,fa[maxn],sum[maxn],res[40],now[maxn];
//sum用来记录连通块大小,res用来记录各个位上的1的个数之和,now来记录当前节点的权值
int find(int x) {//并查集用来记录每个连通块的大小
if(x!=fa[x])return fa[x]=find(fa[x]);
return x;
}
void dfs(int u) {
st[u]=true;
for(auto x:e[u]) {
int a=x.first,b=x.second;
if(st[a]) {//如果已经走过了,则看看是否会发生冲突,若会发生冲突则输出-1
if((now[a]^now[u])!=b) {
cout<<-1<<endl;
exit(0);
}
} else {
now[a]=(now[u]^b);//根据上一个节点的权值得到下一个节点的权值
for(int i=0; i<=30; i++)res[i]+=((now[a]>>i)&1);// 将各个位上的1加入总和中
dfs(a);
}
}
}
void solve() {
cin>>n>>m;
for(int i=1; i<=n; i++)fa[i]=i,sum[i]=1;
for(int i=1; i<=m; i++) {
int x,y,w;
cin>>x>>y>>w;
e[x].push_back({y,w});
e[y].push_back({x,w});
int fx=find(x),fy=find(y);
if(fx!=fy)sum[fx]+=sum[fy],fa[fy]=fx;//计算连通块大小
}
for(int i=1; i<=n; i++) {
if(find(i)!=i)continue;//一个连通块只跑一次
for(int j=0; j<=30; j++)res[j]=0;
dfs(i);
for(int j=0; j<=30; j++)ans+=min(res[j],sum[i]-res[j])*(1<<j);//总共有两种情况,它们是互补的,取min值
}
cout<<ans<<endl;
}