思维 + 模拟:Wonderful Coloring - 2

总结:

temp个不同的值, 每个值都有小于等于k个,那么对这temp *k个数不断的按顺序标注1~k的话那么就不会导致同一个值有两个相同的标注情况。

如果一个值有大于k个,那么按照1~k去标注肯定会存在重复。

题目链接:https://codeforces.com/contest/1551/problem/B2

题目:

有一个元素是整数的数列,有k个颜色去涂这些元素
求涂色方案,需满足以下条件
(1)每一个元素都可以涂成一种颜色或者不涂;
(2)要求数值一样的元素不能涂相同颜色;
(3)并且k个颜色分别涂的总数量要相等
(4)在此基础上我们的方案是让涂色的元素数量最大,可能会有多种方案,输出一种

分析:

1.首先,根据题目的要求可以知道,当某个值出现了k次以上,则k次以上的部分就无法涂上颜色(因为(2)中说了,数值一样的元素不同涂相同的颜色,而不同的颜色最多只能有k个)。而无法涂上颜色的我们用0进行标注。

这样就使得(2)的要求得到了满足

2.随后,我们统计一下还有多少个值没有被标注,求出它们的总和,如果总和(total)不是 k 的倍数,则删去掉多出的部分(total % k)。  为什么要删掉 total % k 个呢 ? 因为我们按照题目的要求,k个颜色,并且每个颜色所涂的块数要相同,所以所能涂的颜色最多也就是 k 的倍数。多出的部分必然不能够涂。   

于是又有一个问题,即然要删掉total % k 个块, 那么删掉哪些块呢?

实际上在剩下的块中,随便删 total % k 个块都可以。

因为我们经过了操作1以后,所有的值都不会出现超过k的情况,那么所有值都可以通过从1~k顺序赋值的方式赋值,并且不会出现重复。

如值为1的块现在还有x1个,值为2的块现在还有x2个,值为3的块现在还有x3个。

而x1 < k , x2 < k , x3 < k.

那么先将x1个 从1~k不断赋值,在对x2不断赋值,在对x3不断赋值,所以不会出现重复的情况。(从而随便删去total % k个块都可以)。

这样就使得(3)的条件得到了满足。

3.随后排序,按照顺序标注1~k的颜色即可。

举例:

举例来说,k为3的话:

1 1 1 2 2

所以最多涂3个。 如1 , 1 , 1涂3种颜色

1 1 1 2 2 2  全部都可以涂。

同样1 1 1 2 2 3也一样可以涂

1 1 1 2 2 3 4 这时,4就涂不了

1 1 1 1 2 2 2这时,多出的1就应该删去。

代码实现:

# include <iostream>
# include <cstring>
# include <algorithm>
using namespace std;

const int N = 2e5 + 10;

struct Node
{
    int v;
    int idx;
}edgs[N];

int a[N];  // 用于统计这个值出现了多少次了,如果超过了k次,则后面的不用上颜色了

int book[N];  // 用于标记颜色从0~k

bool cmp(struct Node a , struct Node b)
{
    return a.v < b.v;
}

int main()
{
    int loop;
    scanf("%d",&loop);
    while(loop--)
    {
        memset(book,-1,sizeof book);  //先让book的颜色为-1,因为后面会对他进行0~k的赋值
        memset(a,0,sizeof a);  // 清空a

        int n,k;
        scanf("%d %d",&n,&k);
        for(int i = 1 ; i <= n ; i++)
        {
            int temp;
            scanf("%d",&temp);
            edgs[i].v = temp;
            edgs[i].idx = i;
            if(a[temp] == k)
            {
                book[i] = 0;  // 给当前这个i节点上0,即不上色。因为已经有k个temp值了,其他的值上不了颜色了。
            }
            else
            {
                a[temp]++;
            }
        }

        sort(edgs + 1 ,edgs + 1 + n, cmp);

        int cnt = 0;

        for(int i = 1 ; i <= n ; i++)
        {
            if(book[i] == -1)
            {
                cnt++;
            }
        }
        if(cnt % k)  // 如果剩下的总值不能够整除k的话,就删去cnt % k个
        {
            int temp = cnt % k;
            int d = 1; // 下标
            while(temp)  // 接下来要删除cnt % k个,从尾巴或者从开头都可以
            {
                if(book[edgs[d].idx] == -1) // 还没有上颜色
                {
                    book[edgs[d].idx] = 0;
                    temp--;
                }
                d++;
            }
        }
        int color = 1; // 接下来开始从1到k涂颜色
        for(int i = 1 ; i <= n ; i++)
        {
            if(book[edgs[i].idx] == -1)
            {
                book[edgs[i].idx ] = color++;
            }
            if(color == k + 1) // 准备用第k + 1 种颜色了,那就变回颜色1
            {
                color = 1;
            }
        }
        for(int i = 1 ; i <= n ; i++)
        {
            printf("%d ",book[i]);
        }
        printf("\n");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值