Codeforces Round #734 (Div. 3) 题解

Codeforces Round #734 (Div. 3) 题解

1551A. Polycarp and Coins

题意:
结账时,一共花费n元,要求只使用1元与2元的硬币进行支付,并且两种硬币的数量之差的绝对值要最小,问使用1元与2元硬币的数量各是多少?

思路:
差值最小的话一元钱和二元钱的数量比例要接近1:1,那么总共三元钱, 那么每份都使用的n/3,最后只要判断一下剩下的钱是0, 1, 2元的情况即可。
如果n%3==0,说明两种硬币数量相等,都为n/3。
如果n%3==1,说明还剩1元需要使用面值为1元的硬币进行支付,那么,1元硬币数量为n/3+1,2元硬币数量为n/3。
如果n%3==2,说明还剩2元需要使用面值为2元的硬币进行支付,那么,1元硬币数量为n/3,2元硬币数量为n/3+1。

AC代码

#include
typedef long long ll;
int t;
ll n;
using namespace std;
int main()
{
    cin>>t;
    while(t--)
    {
      cin>>n;
      if(n%3==0) cout<<n/3<<' '<<n/3<<endl;
      else if(n%3==1) cout<<n/3+1<<' '<<n/3<<endl;
      else cout<<n/3<<' '<<n/3+1<<endl;
    }return 0;
}

1151B1. Wonderful Coloring - 1

题意:
n​​​​​ 个字符要么染成红绿俩种颜色,要么不染色。
相同颜色的字符俩俩不同。
红绿俩种颜色的字符数量相同。
满足以上三个条件,每种颜色被染色的字符数量是多少?

思路:
相同的字符每种颜色最多染一个,因此只要对出现次数 ≥2 的字符和出现一次的字符分别讨论就行。
出现次数 ≥2​ 的字符, 每种颜色都能分到一个,答案加上这些字符的种类数。
出现一次的,红一个,绿一个,答案加上这些字符种类数除以二(向下取整)。

AC代码

#include
using namespace std;
typedef long long ll;
int t;
int main()
{
    cin >> t;
    while(t--){
        string s;
        cin>>s;
        map<char,int>mp;
        for(int i=0;i<s.size();i++) mp[s[i]]++;
        int sum1=0,sum2=0;
        for(auto i : mp)
        {
          if(i.second>=2) sum2++;
          else sum1++;
        }
        cout <<sum2+sum1/2<< endl;
    }
   return 0;
}

1551B2. Wonderful Coloring - 2

题意:

n 个数字,染成 k​ 种颜色,要么不染色。
相同颜色的数字的值是俩俩不同的。
所有的 k 种颜色的数字数量应该相同。
染色的数字的数量要最多。
用 0 - k 表示染色的颜色种类 (0表示不染色) ,求满足上述条件的染色方案。

思路:
考虑将出现次数 ≥k​ 的数字和出现次数
出现次数 ≥k​​ 的字符, 每种颜色都能分到一个, 所以把位置存储下来直接染色对下标进行标记即可,超过 k 的标为0。
出现次数

AC代码

#include
using namespace std;
const int N=2e5+10;
int t,n,k,mp[N];
vector<int>v[N],a;
int main()
{
     ios::sync_with_stdio(false);
     cin.tie(0);
     cin>>t;
     while(t--)
     {
         cin>>n>>k;
         a.clear();
         for(int i=1;i<=n;i++)
         {
            mp[i]=0;  //初始化每个数字标记为0
            v[i].clear();
         }
         for(int i=1;i<=n;i++)
         {
             int x;
             cin>>x;
             v[x].push_back(i);
         }
         for(int i=1;i<=n;i++)
         {
             if(v[i].size()>=k)  //数量超过k时,对前k个进行标记。
             {
              for(int j=0;j<k;j++) mp[v[i][j]]=j+1; //对前k个进行标记
             }
             else  //数量小于k
             {
              for(int j=0;j<v[i].size();j++) a.push_back(v[i][j]); //将其压入a中
             }
         }
         while(a.size()>=k) //进行循环染色
         {
            for(int j=0;j<k;j++)
            {
                mp[a.back()]=j+1;  //对每个数字的末尾进行标记
                a.pop_back();  // 标记完后,进行删除弹出
            }
         }
         for(int i=1;i<=n;i++) cout<<mp[i]<<' ';
         cout<<endl;
     } return 0;
}

1551C. Interesting Story

题意:
从 n 个只包含 a、b、c、d、e 的字符串中选择若干个,使得某一个单一字符的出现次数大于其余四个总和,问最多可以选多少个字符串。

思路:
我们可以记录每个字符串 a、b、c、d、e 出现的次数,然后枚举 a、b、c、d、e 分别作为大于其他四个字符总和的情况。
假设当前枚举 a 第 i 个字符串 a 出现的次数为 sum, 其余四个总和为 now ,那么他们的差值 sum - now 为正的时候是可以选的。为了选择更多,我们贪心的将 sum - now 的值从大到小排序,不断加入字符串, 保持总和 ≥0 即可。

AC代码

#include
using namespace std;
typedef long long ll;
const int maxn =2e5+10;
int a[maxn], b[maxn], c[maxn], d[maxn], e[maxn];
int n;
int get_a(){
    vector<ll> ans;
    for(int i=0;i<n;i++){
        ll sum = a[i];
        ll now = b[i] + c[i] + d[i] + e[i];
        ans.push_back(sum - now);
    }
    sort(ans.rbegin(), ans.rend());
    ll k = 0;
    int cnt = 0;
    for(int i = 0;i < n;i++){
        if(k + ans[i] > 0LL) {
            cnt++;
            k += ans[i];
        }
        else break;
    }
    return cnt;
}
int get_b(){
    vector<ll> ans;
    for(int i = 0;i < n;i++){
        ll sum = b[i];
        ll now = a[i] + c[i] + d[i] + e[i];
        ans.push_back(sum - now);
    }
    sort(ans.rbegin(), ans.rend());
    ll k = 0;
    int cnt = 0;
    for(int i = 0;i < n;i++){
        if(k + ans[i] > 0LL) {
            cnt++;
            k += ans[i];
        }
        else break;
    }
    return cnt;
}
int get_c(){
    vector<ll> ans;
    for(int i = 0;i < n;i++){
        ll sum = c[i];
        ll now = a[i] + b[i] + d[i] + e[i];
        ans.push_back(sum - now);
    }
    sort(ans.rbegin(), ans.rend());
    ll k = 0;
    int cnt = 0;
    for(int i = 0;i < n;i++){
        if(k + ans[i] > 0LL) {
            cnt++;
            k += ans[i];
        }
        else break;
    }
    return cnt;
}
int get_d(){
    vector<ll> ans;
    for(int i = 0;i < n;i++){
        ll sum = d[i];
        ll now = a[i] + b[i] + c[i] + e[i];
        ans.push_back(sum - now);
    }
    sort(ans.rbegin(), ans.rend());
    ll k = 0;
    int cnt = 0;
    for(int i = 0;i < n;i++){
        if(k + ans[i] > 0LL) {
            cnt++;
            k += ans[i];
        }
        else break;
    }
    return cnt;
}
int get_e(){
    vector<ll> ans;
    for(int i = 0;i < n;i++){
        ll sum = e[i];
        ll now = a[i] + b[i] + c[i] + d[i];
        ans.push_back(sum - now);
    }
    sort(ans.rbegin(), ans.rend());
    ll k = 0;
    int cnt = 0;
    for(int i = 0;i < n;i++){
        if(k + ans[i] > 0LL) {
            cnt++;
            k += ans[i];
        }
        else break;
    }
    return cnt;
}
int main()
{
    int t;
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 0;i <= n;i++) a[i] = b[i] = c[i] = d[i]= e[i] = 0;
        for(int i = 0;i < n;i++){
            string s;
            cin >> s;
            for(int j = 0; j < s.size();j++){
                if(s[j] == 'a') a[i]++;
                else if(s[j] == 'b') b[i]++;
                else if(s[j] == 'c') c[i]++;
                else if(s[j] == 'd') d[i]++;
                else if(s[j] == 'e') e[i]++;
            }
        }
        int ans = 0;
       ans= max(ans, get_a());
       ans =max(ans, get_b());
       ans = max(ans, get_c());
       ans = max(ans, get_d());
       ans = max(ans, get_e());
        cout << ans << endl;
    }return 0;
}

1551D1. Domino (easy version)

题意:
在 n * m 的网格图放 1×2 (横着)或者 2×1(竖着) 的多米若骨牌,在 n * m 为偶数的情况下,
是否能恰好放 k 个横着的多米洛骨牌。

思路:

考虑为什么题意给的是 n*m 都是偶数,而不是 n、m 都是偶数。

当 n、m 都为偶数的时候, 2×2​ 的方格可以任意放 2 个 1×2 横着的或者 2×1​ 竖着的,并且它们都是偶数成对出现的,
因此我们就可以分割图面为若干个 2×2的网格, k 为偶数才能满足条件。
假设若 n 为奇数, 画图不难发现必定要放 m/2 个横着的,同理,若 m 为奇数, 必定要放n / 2 个竖着的,多出来的行列铺满横的或者竖的后就转化偶数情况。
因此只要判断总得减去必定放的是否放得下 k 个并且 k 要大于必定放横的数量。

AC代码

#include
using namespace std;
int main()
{
    int t,n,m,k;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>k;
        int temp=n*m/2;
        if(n%2==1)
        {
            k-=m/2;
            temp-=m/2;
        }
        if(m%2==1)
        {
            temp-=n/2;
        }
        if(k<0||k%2==1||k>temp)
        {
            cout<<"NO"<<endl; continue;
        }
        cout<<"YES"<<endl;
    }return 0;
}

1551D2. Domino (hard version)

题意:
在 n ×m 的网格图放 1×2 (横着)或者 2×1(竖着) 的多米若骨牌,在 n×m 为偶数的情况下,是否能恰好放 k 个横着的多米洛骨牌,用字符输出摆放图案。

思路:
按照讨论分别构造即可。
首先先判断有没有奇数的行、列,从 a - z 任意找俩个个字符填充完。
然后将这多的行列暂时删去,当成 n,m 都为偶数填充。
先填充 k 个横着的,剩下的 2×2 填充竖着的即可,唯一的问题就是相邻的字符会重复的问题,我们可以根据下标的奇偶关系来交替填充不同​字符,字符从 a−z 自己挑。

如果觉得写的还不错,点个赞吧^ ^