【补题】codeforces的wonderful coloring的超详细题解

题目描述

题目释义

字符串s的每一个字符颜色都是红绿白之一

规则:

  1. 颜色相同的字符均两两不相等
  2. 红绿颜色字符数量相同
  3. 能涂的都要涂

算法设计

如果暴力求解,则遍历所有情况为3的50次方,远远超时

观察得到涂的什么颜色并不影响判断,只需知道字符有没有被涂色。规则等价于

  1. 涂色的数量为偶数
  2. 涂色的字符中相同的字符不超过2个

若同时满足以上两个规则,则必然可以找出至少一种满足题设要求的涂色方案。

因为只需要:

  1. 将成对出现的字符一个涂成红色,一个涂成绿色
  2. 剩下的单个出现的字符总数必然是个不相等且共有偶数个,那么一半涂红一半涂绿

这样就得到了一个wonderful coloring

例:1表示红2表示绿

kzaaa,知道了11101

那就一个a涂红一个a涂绿,kz一个涂红一个涂绿

所以kzaaa 21102、12201等等都是wonderful coloring

所以只需一个数组标志每个字符涂没涂,判断

  1. 有偶数个1
  2. 同一个字符最多出现2次

由于要求的是被涂元素个数最多的情况,先考虑有小于s.length的最大偶数n个元素被涂色,若没找到最佳方案,n一次-2即可。

如何遍历?

问题相当于遍历从a1,a2,……an中选取k个元素的所有情况,选k个就需要k层循环,这基本上不可能简单的实现

进一步思考,只需要:

  1. 选取偶数个成单元素涂色
  2. 选取任意数量的成对元素

例如pakawdac,只需要列出一个表记录每个字符出现的个数:

p,k,w,d,c各出现1次,a出现3次

则只需要从pkwdc五个中选4个涂色,选一对aa涂色即可。结果就是有三个涂成了红色。

记ou(a)表示不大于a的最大偶数

所以只需要记录:

n元素出现了1次,k个元素出现了多次

最终结果就是ou(n)/2+k

更进一步,只需要记录出现超过两次的字符,出现单次的字符用字符串长度减就可以了。

**算法关键: **

数据处理

每一组输入存入s[50],分别进行运算,结果储存在ans[T]中

    int T;
    cin >> T;
    int ans[T];
    int l = 0;
    for (int i = 0;s i < T; ++i) {
        char s[50];
        cin >> s;
        ans[l++] = solve(s);
    }

运算逻辑

函数solve:输入字符串

  1. num为单次出现字符数量,res为多次出现字符数量,ans为答案
  2. 因为数组删除元素困难,定义repeat专门存放已经计入结果的字母,当i指向下一个字符后判断该字符是否是repeat中的元素,如果是直接goto开启下一个循环即可
  3. flag表示该字符出现过1次还是多次,以便对res或num进行增加。
  4. 核心:但凡找到一个s[j]=temp,直接break并将该字母记为重复。
int solve(const char *s) {
    int ans;
    int res = 0;
    int num = 0;
    char repeat[100];
    int reLen = 0;
    int flag = 1;//0多次出现1多出现一次
    for (int i = 0; s[i]; ++i) {
        char temp = s[i];
        for (int j = 0; j < reLen; ++j) {
            if (repeat[j] == temp) {
                goto end;
            }
        }
        for (int k = i + 1; s[k]; ++k) {
            if (s[k] == temp) {
                flag = 0;
                res++;
                repeat[reLen++] = temp;
                break;
            }
        }
        if (flag) {
            num++;
        }
        flag = 1;
        end:;
    }
    if (num % 2) { num--; }
    ans = res + num/2;
    return ans;
}

输出处理

for (int i = 0; i < l; ++i) {
    cout << ans[i] << endl;
}

总结

  1. 对于多组输入分开处理的input,建立答案数组储存每一个的答案。
  2. 通过对题意的分析得出了高效的算法。心路历程始终是:将逻辑陈述简化并逐步转变为可操作型语言。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵寒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值