Codeforces Round 479 (Div. 3)

比赛链接

A. Wrong Subtraction
给一个数n,俩操作执行k次:

  • 个位不为0就减1
  • 个位为0就除10

直接模拟,代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,k;cin>>n>>k;
    while(k--){
        if(n%10!=0) n--;
        else n/=10;
    }
    cout<<n;
    return 0;
}

B. Two-gram
给个字符串找字符对(无要求),使得该字符对在串中出现次数最大

先预处理出所有的字符对,再到串中找出现次数,暴力比较每一个字符对出现次数,代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;cin>>n;
    string s;
    vector<string> a(n);
    for(auto& c:s) cin>>c;
    for(int i=0;i+1<n;i++){
        a[i]+=s[i];
        a[i]+=s[i+1];
    }
    int mx=0;
    string ma;
    for(int i=0;i<n-1;i++){
        int cnt=0;
        for(int j=0;j<n-1;j++){
            if(a[i]==a[j]) cnt++;
            if(cnt>mx){
                mx=cnt;
                ma=a[j];
            }
        }
    }
    cout<<ma;
    return 0;
}

C. Less or Equal
给一个n长数组a,寻找一个数x使得数组中只有k个数大于或等于这个数
先排序,再考虑特殊情况
特判较多

  • 对数组排序之后判断k和k+1位置是否相等(相等则在该处有两次大于等于x)
  • k>n(找不全)
  • k=0时,若a[0]=1,则找不到一个x使k=0(即必有一数大于x),若a[0]!=1则x=1(比a[0]小都行)

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,k;cin>>n>>k;
    vector<int> a(n);
    for(auto& aa:a) cin>>aa;
    sort(a.begin(),a.end());
    if(a[k-1]==a[k]||k>n||(k==0&&a[0]==1)) cout<<"-1";
    else if(k==0) cout<<"1";
    else cout<<a[k-1];
    return 0;
}

D. Divide by three, multiply by two
给一个数组,由原本数组打乱,找到原数组
原数组是一个由一个数x经历n-1次操作变成的n长数组a,操作为:

  • x分三份(即除3)
  • x乘2

乱序的序列,我们需要找出它们被依次书写的顺序,使得每个数都可以通过上述操作从前一个数得到 由于最终的顺序是一个单链,我们可以将数字看作一个有向图,其中边表示可以进行的转换(除以 3 或乘以 2)我们需要找到这个链的起点(即 最终的最大数或者最小数)。
通过 DFS依次构造这个链。
解法思路
建图:
遍历所有数字,对于每对数字 (a[i], a[j]):
如果 a[i] * 2 =a[j],建立一条 i → j 的边。
如果 a[i] / 3 =a[j] 且 a[i] % 3 ==0,建立 i → j 的边。

  • 找到起点:链的起点只能是某个 没有前驱的节点,即没有其它数字可以通过乘 2 或除 3 得到它。
  • 构造正确的顺序:从起点出发,沿着构造的路径(按照规则)依次寻找下一个数,直到遍历所有数。

最后以O(n)时间解决 代码如下:

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main(){
    int n;cin>>n;
    vector<ll> a(n);
    unordered_map<ll,int> p;//数与索引
    unordered_map<ll,int> ind;//数与入度
    for(int i=0;i<n;i++){ 
        cin>>a[i];
        p[a[i]]=i;
        ind[a[i]]=0;
    }
    //邻接表
    unordered_map<ll,vector<ll>> ne;
    for(int i=0;i<n;i++){
        ll tmp=a[i];
        if(p.count(tmp*2)){
            ne[tmp].push_back(tmp * 2);
            ind[tmp*2]++;
        }
        if(tmp%3==0&&p.count(tmp/3)){
            ne[tmp].push_back(tmp / 3);
            ind[tmp/3]++;
        }
    }
    //找入度为0的点
    ll start=-1;
    for(ll& aa:a){
        if(ind[aa]==0){
            start=aa;
            break;
        }
    }
    //顺着表输出
    for(ll i=start,cnt=0;cnt<n;cnt++) {
        cout<<i<<' ';
        if (!ne[i].empty()) {
            i = ne[i][0]; // 确保从邻接表中取出下一个节点
        }
    }
    cout<<'\n';
    return 0;
}

E. Cyclic Components
给定了一个无向图,要求计算出图中所有的 环形联通分量 的数量。图的一个联通分量是一个环形联通分量,当且仅当该分量包含的每个节点的度数都为 2,并且所有的边能构成一个闭环

环形联通分量的定义
一个联通分量是 ,当且仅当:

  1. 该分量的所有节点的度数都为 2(每个节点都正好与两个节点相连)
  2. 该分量的所有节点和边可以构成一个环

解题思路

  • 通过 DFS 遍历图,找出所有的连通分量
  • 对每个连通分量,检查所有节点的度数是否均为 2
  • 如果是环,则计数

代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n + 1);// 邻接表
    vector<int> de(n + 1, 0);// 存度数
    vector<int> vi(n + 1, 0);// 标记节点
    // 构建图,统计每个节点的度数
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
        de[u]++;
        de[v]++;
    }
    int cnt = 0;
    //DFS
    auto dfs = [&](int x, vector<int>& com, auto&& dfs_ref) -> void {
        vi[x] = 1;
        com.push_back(x);
        for (auto& nx : g[x]) {
            if (!vi[nx]) {
                dfs_ref(nx, com, dfs_ref);
            }
        }
    };
    // 遍历进行DFS
    for (int i = 1; i <= n; i++) {
        if (!vi[i]) {
            vector<int> com;
            dfs(i, com, dfs);
            // 检查是否为环
            bool ishuan = true;
            for (int nd : com) {
                if (de[nd] != 2) {
                    ishuan = false;
                    break;
                }
            }
            if (ishuan) {
                cnt++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

F. Consecutive Subsequence
给定的数组中选择一个最长的递增连续子序列。这个子序列应该是按顺序的连续整数(比如 [x, x+1, x+2, ..., x+k-1])。我们需要输出这个子序列的长度以及对应的数组索引
解题思路

  • 动态规划的思想:我们可以使用一个map来记录每个数能形成的最大递增连续子序列的长度。具体来说,对于数组中的每个元素 a[i],如果 a[i] - 1 这个数字已经出现过,那么 a[i] 可以接在 a[i] - 1 后面形成一个新的连续子序列。因此,我们可以更新 a[i] 所能形成的最大子序列长度为 f[a[i] - 1] + 1
  • 回溯得到答案: 一旦我们找到了最长的递增连续子序列的长度,就可以从数组的末尾开始回溯,找到该子序列的索引
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<int> a(n + 1); 
    map<int, int> f;  // 用来存储每个数值的最大递增子序列长度
    vector<int> ans;  // 存储最终的索引
    int mxn = 0, mx = 0;  // mxn为最大子序列长度,mx为对应的数字
    for (auto& aa:a) cin >> aa;
    // 动态规划计算每个数字的最大递增子序列长度
    for (int i = 1; i <= n; i++) {
        f[a[i]] = f[a[i] - 1] + 1;  // 更新递增子序列的长度
        // 更新最长递增子序列的长度及其对应的数字
        if (f[a[i]] > mxn) {  
            mxn = f[a[i]];
            mx = a[i];
        }
    }
    cout << mxn << '\n';
    // 从数组末尾回溯,找到最大递增子序列的索引
    for (int i = n; i >= 1; i--) {
        if (a[i] == mx) {
            ans.push_back(i); // 找到的索引加入结果
            mx--;  // 下一个目标数字
        }
    }
    for (int i = ans.size() - 1; i >= 0; i--) {
        cout << ans[i] << " ";
    }
    return 0;
}
posted @   fufuaifufu  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示