AtCoder 第399场初级竞赛 A~E题解

 A Hamming Distance(汉明距离)

【题目链接】

原题链接:A - Hamming Distance

【考点】

判断,循环

【题目大意】

计算两段字符串相同位置,有多少个字符不相同。

【解析】

枚举字符串,如果字符不同ans++。

【难度】

GESP二级

【代码参考】

#include
using namespace std;

int main(){
	string s1, s2;
	int n, ans = 0;
	cin >> n >> s1 >> s2; 
	for(int i = 0; i < s1.size(); i++){
		if(s1[i] != s2[i]) ans++;
	}
	cout << ans;
	return 0;
}

B Ranking with Ties(有并列的排名)

【题目链接】

原题链接:B - Ranking with Ties

【考点】

选择排序

【题目大意】

每个人 i 有得分 pi,对每个人的得分进行排名,得分相同的排名相同。

【解析】

使用选择排序的思想,找到未排序的最大值,再通过最大值给等于最大值的得分赋值排名并标记为已排名,最后输出排名即可。

【难度】

GESP四级

【代码参考】

#include
using namespace std;

int main(){
	int n, a[105], vis[105], ans[105], r = 1;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++){
		int k = 0;
		int maxn = 0;
		for(int j = 1; j <= n; j++){
			if(vis[j]) continue;
			if(maxn < a[j]) maxn = a[j];
		}
		for(int j = 1; j <= n; j++){
			if(maxn == a[j]){
				ans[j] = r;
				k++;
				vis[j] = 1;
			}
		}
		r += k;
	}
	for(int i = 1; i <= n; i++){
		cout << ans[i] << endl;
	}
	return 0;
}

C Make it Forest(使之成为森林)

【题目链接】

原题链接:C - Make it Forest

【考点】

并查集

【题目大意】

给一个简单无向图,最少删除几条边使其可以变成森林(无环图)。

【解析】

使用并查集判断两点是否在一个联通量中,如果存在那么两点连接的边则需要删除。

【难度】

GESP六级

【代码参考】

#include
using namespace std;

const int N = 2e5 + 5;
int n, m, fa[N], sum;

int find(int x){
	return fa[x] = (fa[x] == x ? x : find(fa[x])); 
}

int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		if(find(u) == find(v)){
			sum++;
		}
		else{
			fa[find(u)] = find(v);
		}
	}
	cout << sum;
	return 0;
}

D Switch Seats(交换座位)

【题目链接】

原题链接:D - Switch Seats

【考点】

STL(map)

【题目大意】

你要找出满足以下条件的整数对 (a, b) 的数量:在序列 A 中,a 的两次出现不相邻。在序列 A 中,b 的两次出现不相邻。通过有限次交换操作(选择 i 和 j,交换 A[i] 和 A[j]),可以使 a 的两次出现相邻,同时 b 的两次出现也相邻。

【解析】

使用map记录每一对(相邻)的位置,如果出现过相同,或逆序且不在上一个位置,则认为出现一对。

【难度】

GESP五级

【代码参考】

#include
using namespace std;

const int N = 5e5 + 5;
int a[N], n, ans, t;
// 定义一个 map,键为元素对(pair),值为该元素对最后一次出现的位置
// 用于记录序列中相邻元素对及其出现的位置信息
map, int> m;

int main(){
    cin >> t;
    while(t--){
        cin >> n;
        n *= 2;
        for(int i = 1; i <= n; i++) cin >> a[i];
        m[{a[1], a[2]}] = 2;  // 初始化 
        for(int i = 3; i <= n; i++){
            // 检查当前相邻元素对 {a[i], a[i + 1]} 是否已经在 map 中出现过
            // 或者其逆序元素对 {a[i + 1], a[i]} 在 map 中出现过,并且其最后一次出现的位置不是上一次相邻的位置(即不是 i - 1)
            if(m.count({a[i], a[i + 1]}) || (m.count({a[i + 1], a[i]}) && m[{a[i + 1], a[i]}] != i - 1)){
                // 如果满足上述条件,说明找到了一组满足要求的情况
                // 即这两对情侣原本不相邻,但可以通过交换座位最终相邻
                ans++;
            }
            // 记录当前相邻元素对 {a[i - 1], a[i]} 及其出现的位置
            m[{a[i - 1], a[i]}] = i;
        }
        cout << ans << endl;
    }
    return 0;
}	

E Replace(替换)

【题目链接】

原题链接:​​​​​​​E - Replace

【考点】

深度优先搜索dfs、数组计数。

【题目大意】

要求判断是否可以通过重复  “选择两个小写英文字母 xy,并将字符串 S 中的每个 x 替换为 y”  这一操作,使得字符串 S 与字符串 T 相同。若可以,还需找出所需的最小操作次数。

【解析】

整体思路是先构建字符替换映射,检查是否存在冲突,然后判断是否存在无法解决的情况,接着通过深度优先搜索(DFS)来处理字符替换的循环,最后计算出最小操作次数。

【难度】

GESP六级

【代码参考】

#include
using namespace std;

// ans 用于记录字符替换形成的循环数量
// x 作为深度优先搜索时记录的起始字符
// v[128] 标记字符是否在深度优先搜索中被访问过
// sum 统计有效的字符替换数量
int n, ans, x, v[128], sum, f[128];
string s, t;
// m[128] 存储字符替换的映射关系,m[s[i]] 表示将 s 中的字符 s[i] 替换为 t 中对应的字符
char m[128];

// 深度优先搜索函数,用于检测字符替换形成的循环 
void dfs(int d) {
    // 若当前字符没有替换目标(值为 0),则结束本次搜索
    if (d == 0) return;
    // 若当前字符已经被访问过
    if (v[d]) {
        // 若当前字符等于起始字符 x,说明形成了一个循环,循环数量 ans 加 1
        if (x == d) ans++;
        return;
    }
    // 标记当前字符为已访问
    v[d] = 1;
    // 递归调用 dfs 函数,继续搜索当前字符的替换目标 
    dfs(m[d]);
}

int main() {
    cin >> n;
    cin >> s >> t;
    for (int i = 0; i < n; i++) {
        // 若字符 s[i] 已经有替换目标,且该目标与 t[i] 不相等
        if (m[s[i]] && m[s[i]] != t[i]) {
            // 说明无法通过规定操作使 s 与 t 相同,输出 -1 并结束程序 
            cout << -1;
            return 0;
        }
        m[s[i]] = t[i];// 记录字符 s[i] 的替换目标为 t[i]
        f[t[i]] = 1;// 标记目标字符串 t 中出现的字符
    }
    int cnt = 0;
    for(int i = 'a'; i <= 'z'; i++){
        // 累加目标字符串 t 中出现的不同字符数量
        cnt += f[i];
    }
    // 若目标字符串 t 包含所有 26 个小写字母,且 s 与 t 不相等
    if(cnt == 26 && s != t){
        // 意味着没有可用的中间字符进行替换,输出 -1 并结束程序
        cout << -1;
        return 0;
    }
    // 定义一个二维向量 a,a[i] 用于记录有替换目标为 i 的所有字符
    vector a[128];
    for(int i = 'a'; i <= 'z'; i++){
        // 若字符 i 的替换目标是它本身,将其替换目标置为 0,表示无需替换
        if (m[i] == i) m[i] = 0;
        // 若字符 i 有替换目标,sum 加 1,sum 记录有效的字符替换数量
        sum += (bool)(m[i]);
        // 将字符 i 加入到其替换目标的入边列表中
        a[m[i]].push_back(i);
    }
    for(int i = 'a'; i <= 'z'; i++){
        // 若字符 i 的入边数量大于等于 2,即有多个字符要替换成 i
        if (a[i].size() >= 2)
            if (!v[i])
                dfs(i);
    }
    for(int i = 'a'; i <= 'z'; i++){
        if(!v[i]){
            // 记录当前搜索的起始字符
            x = i;
            dfs(i);
        }
    }
    // 输出最小操作次数,即循环的数量 ans 加上有效的字符替换数量 sum
    cout << ans + sum;
    return 0;
}

你可能感兴趣的:(青少年编程比赛题解,算法,c++,AtCoder)