Codeforces Round #844:C. Equal Frequencies

一、来源:Problem - C - Codeforces

二、题面

image-20230219142421452

三、思路

  1. 先考虑一个子问题模型:我们现在有用\(m_1\)种随机字母组成的n个数,各字母个数未定,现在需要使这n个数变为\(m_2\)种类,平均每个种类为blance=n/\(m_2\)个数,问如何求得最小的改变数

    • 显然,只能由多的数向少的数改变,为此我们先尝试求出所有所有字母出现的次数并对其进行降序排序得到vector<pair<int,int>> v;\\pair的first为字母小标(由0开始),second为字母出现的个数

    • 我们考虑转换的过程,其实就是高位-1,低位+1的过程.

      • 尝试只找需要减去的位,初一看我们在前面高位用v[i].second减去blance直到v[i].second<=blance即可,然而事实上这情况讨论过的前提是\(m_1<m_2\);当\(m_1>m_2\),以数列9,3,3,3,2为例,我么将其转为为5,5,5,5,,最前面的9需要减去,后面的2也需要减去,这就比较复杂了
      • 尝试只找需要增加的位:根据上面错误的经验,我们可以得到需要增加的部分都是在m2之前的,且都可以使用表达式ans+=blance-v[i].second.经代码验证可以得到复合题意的解
    • code

      #include<bits/stdc++.h>
      
      using namespace std;
      
      bool cmp(const pair<int,int> a,pair<int,int> b){
          return a.second > b.second;
      }
      
      char toChar(int offset){
          return 'a'+offset;
      }
      
      int toInt(char c){
          return c-'a';
      }
      
      int main(){
          int t;
          cin >> t;
          while(t--){
              int n,m2;  //n个数转换为m种,每种n/m个
              char str[100];
              cin >> n >> m2;
              cin >> str;
              map<int,int> mp;
              for(int i=0;i<26;i++){  //需初始化
                  mp[i]=0;
              }
              int m1=0;
              for(int i=0;i<n;i++){
                  if(++mp[toInt(str[i])]==1){
                      m1++;
                  }
              }
              vector<pair<int,int>> v(mp.begin(),mp.end());
              sort(v.begin(),v.end(),cmp);
              int ans=0,blance=n/m2;
              for(int i=0;i<m2;i++){
                  if(v[i].second<blance){
                      ans+=blance-v[i].second;
                  }
              }
              cout << ans << endl;
          }
          return 0;
      }
      

    注:该问题模型其实还可以简化为:n个和为sum的数,每次加一减一,通过加减操作转化为m个相同的数,每个数为sum/m,所需要的最少加减次数

  2. 将问题分解:本题注意到小写字母是可以遍历的,我们可以遍历26种可能的情况求得变化最小的一次解的值,然后再对字符串进行变换

    • 遍历26种可能并求得变化最小的一个解
    • 模拟求变换即可(这里的代码属于是暴力了)
  3. 补充:对于toChar,toInt,最好还是封装为函数

四、代码

#include <bits/stdc++.h>
#define eleType int
#define INF 0x3f3f3f3f

typedef long long ll;

using namespace std;

//字母遍历
//设字母总数为n,平等值为m => n<26*m且n%m==0

const int N=1e5+10;
char arr[N];
map<int,int> mp;

bool cmp(const pair<int,int> a,pair<int,int> b){
    return a.second > b.second;
}

char toChar(int offset){
    return 'a'+offset;
}

int toInt(char c){
    return c-'a';
}

int main(){
    int t;
    cin >> t;
    while(t--){
        eleType n;
        cin >> n >> arr;
        for(int i=0;i<26;i++){  //需初始化
            mp[i]=0;
        }
        int m1=0;
        for(int i=0;i<n;i++){
            if(mp[toInt(arr[i])]++==0){
                m1++;
            }
        }
        vector<pair<int,int>> v(mp.begin(),mp.end());
        sort(v.begin(),v.end(),cmp);
        eleType ans=INF,m2=1,blance;
        for(int i=1;i<=26;i++){
            if(n%i==0){  //i是种类,不是个数,不需要保证26*i>=n
                // cout << "i:" << i << endl;
                eleType temp=0,now=0;
                blance=n/i;
                for(int j=0;j<i;j++){
                    //cout << "mp[" << j << "]:" << mp[j] << endl;
                    if(v[j].second<blance){
                        temp+=blance-v[j].second;
                    }
                }
                // cout << temp << endl;
                if(temp<ans){
                    ans=temp;
                    m2=i;
                }
                // cout << "i:" << i << " temp:" << temp << endl;
            }
        }
        blance = n/m2;
        eleType next=0;
        for(int i=0;i<v.size();i++){
            if(v[i].second<blance){
                next=i;
                break;
            }
        }
        // cout << m2 << endl;
        for(int i=0;i<n;i++){
            eleType now;
            for(int j=0;j<v.size();j++){
                if(arr[i] == toChar(v[j].first)){  //易错漏,转换应封装为函数
                    now=j;  //排序后的位置
                }
            }
            if(v[now].second>blance||now>=m2){  //多余数,后者所在的种类位次超过m2
                //在前面或后面
                v[now].second--;
                // cout << "v[next].first:" << v[next].first << endl;
                arr[i]=toChar(v[next].first);
                if(++v[next].second==blance){
                    next++;
                }
                
            }
        }
        cout << ans << endl;
        cout << arr << endl;
    }
    return 0;
}
posted @ 2023-02-19 15:35  Arno_vc  阅读(46)  评论(0编辑  收藏  举报