[算法学习记录][题解] ABC399 A-D
A - Hamming Distance
A题就是统计两个字符串不同的字符的数量,直接暴力就行。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
int n;cin >> n;
string s,t;cin >> s >> t;
int ans = 0;
for(int i = 0;i<n;i++) if(s[i]!=t[i]) ans++;
cout << ans <<'\n';
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
B - Ranking with Ties
就是一个确定排名的问题,且数据量较小,直接O(n2)暴力就行,观察样例,可以发现每个数字的排名就是数组中比这个数字大的数字的数量加一。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 105;
int p[N],loc[N];
void solve()
{
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> p[i];
int r = n;
for(int i = 1;i<=n;i++)
{
int count = 0;
for(int j = 1;j<=n;j++) if(p[j]>p[i])count++;
loc[i] = count+1;
}
for (int i = 1; i <= n; i++) cout << loc[i] << "\n";
//输出坐标
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int _ = 1;
while (_--)solve();
return 0;
}
C - Make it Forest
我们可以用并查集或者DFS来判断一个图中是否存在环;
并查集:
并查集的实现我已经在之前的一篇博客中说过了,不了解的可以看一下我的博客 点击跳转,并查集是用来处理图上节点的连接关系的,在读入边时,存在两种情况:
- 两者的父节点不相同;
说明两点间并没有形成通路,就算把他们连接在一起也不存在环; - 两者的父节点相同;
说明两节点间已经存在通路,所以这时如果再把他们连接在一起就会形成环。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5+5;
int pre[N];
//每个节点的父节点
int root (int x)
{
return pre[x] = (pre[x] == x ? x : root(pre[x]));
}
//寻找父节点
void solve()
{
int count = 0;
int n,m;cin >> n >> m;
for(int i = 1;i<=n;i++) pre[i] = i;
//初始化父节点数组,每个节点的默认父节点为其本身
while(m--)
{
int u,v;cin >> u >> v;
int rootu = root(u);
int rootv = root(v);
if(rootu==rootv) count++;
//如果两个节点的父节点相等,说明两节点间已经存在一条通路,再把两者连接起来就会形成环,此时不需要将两节点连接。
else pre[rootu] = rootv;
//否则就把这两个节点连接
}
cout << count <<"\n";
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
DFS
本题的另一个做法就是DFS,DFS有一个
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5+5;
vector<int> mp[N];
bitset<N> vis;
//标记经过的顶点
int c = 0;
void dfs(int st,int y)
{
vis[st] = true;
for(auto &v : mp[st])
{
if(!vis[v]) dfs(v,st);
//如果这个点没被访问,递归执行dfs函数访问这个节点
else if(v!=y) c++;
//如果当前边等于他的父节点只能说明存在重边,不等于父节点,说明通往父节点的路不止一条,说明存在环
}
}
//利用dfs的回溯判断环的个数
void solve()
{
int n,m;cin >> n >> m;
while(m--)
{
int u, v;cin >> u >> v;
mp[u].push_back(v);
mp[v].push_back(u);
}
//用邻接表存储图
for(int i = 1;i<=n;i++) if(!vis[i]) dfs(i,-1);
//确保对每个节点进行dfs操作,由于i是父节点,父节点没有自己的父节点,所以为dfs(i,-1)
cout << c/2 <<"\n";
//有向图就不用除以2了,无向图每条边有两个方向,导致每个环会被统计两次,所以需要除以2
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
D - Switch Seats
本题中,每个数字都有两个假设a,b的数对都不相邻我们就要判断a,b经过交换后能否让两个a与两个b相邻;
本题的数据量较大,暴力判断肯定会超时;
所以我们可以尝试枚举一个a[i],并判断其与a[i+1]组成的数对是否合法;
为了减小枚举数字时的时间复杂度,我们把每种数字的坐标都存在一个数组中,这样,每枚举到一个数字,就能快速判断其是否符合条件;
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 4e5+10;
int a[N];
void solve()
{
int n;cin >> n;
for(int i = 1;i<=2*n;i++)cin >> a[i];
vector<vector<int>> pos(n+1);
//不建议使用vector<int> pos[N];因为这样不仅会有巨大的内存开销,还会导致缓存命中率降低导致时间复杂度升高
set<pair<int,int>> st;
//用来存储符合条件的数对
for(int i = 1;i <= 2*n;i++) pos[a[i]].push_back(i);
//记录每种数字的坐标
for(int i = 1;i < 2*n;i++)
{
int x = a[i],y = a[i+1];
if(pos[x][0]+1 == pos[x][1]) continue;
if(pos[y][0]+1 == pos[y][1]) continue;
//剔除掉相同数字相邻的情况
vector<int>v{pos[x][0],pos[x][1],pos[y][0],pos[y][1]};
sort(v.begin(),v.end());
//剔除了相邻数字相邻的情况,所以经过排序后一定是按这样的形式排列(pa1,pb1,pa2,pb2)其中pa1,pa2,pb1,pb2都是a,b的坐标
if(v[0]+1==v[1]&&v[2]+1==v[3])st.insert({min(x,y),max(x,y)});
//只要满足v[0],v[1]相邻,v[2][3]相邻,也就是pax与pbx相邻,pay与pby相邻x,y为1或2
//第一位存最小值,第二位存最大值可以防止重复
}
//枚举
cout << st.size() <<"\n";
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
//关闭同步流
int _; cin >> _;
//多个样例
while(_--)solve();
return 0;
}
本人初学算法,难免有纰漏,如若发现,敬请指正。

浙公网安备 33010602011771号