2020牛客暑期多校训练营Mask Allocation(欧几里得算法)

本文介绍了牛客暑期多校训练营中的一道题目——Mask Allocation,涉及如何将n*m矩阵分组,使其既能按n等分也能按m等分,并输出字典序最大的方案。通过分析题目,作者提出了类似辗转相除法的解决方案,通过举例和代码展示了思路和解题过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mask Allocation

题目描述

在这里插入图片描述

输入描述:

在这里插入图片描述

输出描述:

在这里插入图片描述

示例1

输入

2
5 4
3 3

输出

8
4 4 4 4 1 1 1 1
3
3 3 3

题目大意

给定一个 n n n m m m,要求输出一种把 n ∗ m n*m nm分组的方案,使得这个分组的方案中能合并元素变成 n n n等分也能变成 m m m等分。输出分组的方案中字典序最大的那个,并在此之前输出分组的个数。
如: 5 , 4 5,4 5,4
4 , 4 , 4 , 4 , 1 , 1 , 1 , 1 4,4,4,4,1,1,1,1 4,4,4,4,1,1,1,1可以把后面 4 4 4 1 1 1分别加到前面的 4 4 4上,变成 4 4 4等分 ( 5 , 5 , 5 , 5 ) (5,5,5,5) (5,5,5,5),也可以将后面的 1 1 1全部合并起来变成 4 4 4实现 5 5 5等分 ( 4 , 4 , 4 , 4 , 4 ) (4,4,4,4,4) (4,4,4,4,4)

分析

灵光一闪

这题比赛的时候就做出来了,当时灵光一现就感觉是这么做的,找了几组数据就AC了。
先说说比赛的时候我的做法。
先列出两个数列,分别是全为 n n n的长为 m m m的和全为 m m m的长为 n n n的。然后将它们上下对齐。
下面以 5 , 3 5,3 5,3为例:
5 , 5 , 5 5,5,5 5,5,5
3 , 3 , 3 , 3 , 3 3,3,3,3,3 3,3,3,3,3
然后将相等的长度部分都减去较小的部分。
2 , 2 , 2 2,2,2 2,2,2
0 , 0 , 0 , 3 , 3 0,0,0,3,3 0,0,0,3,3
然后将 0 0 0的部分去掉。
2 , 2 , 2 2,2,2 2,2,2
3 , 3 3,3 3,3
然后将上面减掉的长度存入答案。即 a n s = { 3 , 3 , 3 } ans=\{3,3,3\} ans={3,3,3}。然后再进行上面的操作。即:
0 , 0 , 2 0,0,2\qquad 0,0,2 1 , 1 1,1\qquad 1,1 a n s = { 3 , 3 , 3 , 2 , 2 } ans=\{3,3,3,2,2\} ans={3,3,3,2,2}//减去相同
2 2\qquad 2 1 , 1 1,1\qquad 1,1 a n s = { 3 , 3 , 3 , 2 , 2 } ans=\{3,3,3,2,2\} ans={3,3,3,2,2}//去掉0,下同,直到都是0
1 1\qquad 1 0 , 1 0,1\qquad 0,1 a n s = { 3 , 3 , 3 , 2 , 2 , 1 } ans=\{3,3,3,2,2,1\} ans={3,3,3,2,2,1}
1 1\qquad 1 1 1\qquad 1 a n s = { 3 , 3 , 3 , 2 , 2 , 1 } ans=\{3,3,3,2,2,1\} ans={3,3,3,2,2,1}
0 0\qquad 0 0 0\qquad 0 a n s = { 3 , 3 , 3 , 2 , 2 , 1 , 1 } ans=\{3,3,3,2,2,1,1\} ans={3,3,3,2,2,1,1}
退出。发现 a n s ans ans就是答案,而且是字典序最大的。
当时是这么做了,也是冥冥之中有点感觉。也没仔细想过。
那么AC代码就放在这里,大家自己理解。

正解

接下来是正解。
首先我们考虑前几个的答案。假如我们默认 n ≥ m n\ge m nm,那么前 n − n   m o d   m n-n\,mod\,m nnmodm个答案肯定是 m m m。因为如果大于 m m m,那么无法变成 n n n等分,小于的话字典序没有 m m m大。所以我们可以把前 m m m个填掉。
那么接下来,我要把剩下的几个填掉,那么,考虑需要满足什么。如果我这个填完之后,满足可以构成 n   m o d   m n\,mod\,m nmodm m m m以及能构成 m m m n n n就可以了。结果发现求解过程是类似于辗转相除法的,所以只要跑一趟 g c d gcd gcd就可以AC了。
与上面的对照,发现其实根本是一样的,上面的比较容易想到,而正解是每一步都是实的,比较稳。上面数组相减其实和这个辗转没有太大的区别,看官们可以结合起来啃啃。

代码

#include<bits/stdc++.h>
#define ll long long
#define inf 1<<30
using namespace std;
vector<int> ans;
void gcd(int n,int m)
{
    if(m==0) return;
    for(int i=0;i<(n/m)*m;i++) ans.push_back(m);
    //存储答案
    gcd(m,n%m);
}//gcd
int main()
{
    int t,n,m,i;
    scanf("%d",&t);
    while(t--)
    {
        ans.clear();
        scanf("%d%d",&n,&m);
        if(n<m) swap(n,m);//nm的顺序没有关系,为了代码的方便,交换成n>=m
        gcd(n,m);
        printf("%d\n",ans.size());
        for(i=0;i<ans.size();i++)   
            printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}

END

简单的规律题,其实很容易想到的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值