2020暑期牛客多校训练营第七场(B)Mask Allocation(gcd/暴力)

Mask Allocation

原题请看这里

题目描述:

n ∗ m n*m nm个口罩需要分配。已知共有 2 2 2种医院,分别为 n n n家重症医院和 m m m家移动轻症医院。这些口罩需要被打包成盒装送去医院,但是并不知道应该送去重症医院还是轻症医院,因此应该将口罩打包成能被分为每组 n n n个共 m m m组,和每组 m m m个共 n n n组的形式。
请输出口罩盒数量最小,且字典序最大的方案

输入描述:

有多个测试用例。 输入的第一行包含一个整数 T ( 1 ≤ T ≤ 100 ) T(1 \leq T \leq 100) T(1T100),表示测试用例的数量。 对于每个测试用例,输入的唯一行包含两个整数 n , m ( 1 ≤ n , m ≤ 1 0 4 ) n,m(1 \leq n,m \leq 10 ^ 4) nm(1nm104),代表重症医院和轻症医院的数量。

输出描述:

对于每个测试用例,请输出两行。 第一行应在第一行中包含一个整数 k k k,表示最少的盒子数。 在第二行中,请输出 k k k个整数,表示按字典顺序的最大序列。

样例输入:

2
5 4
3 3

样例输出:

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

思路:

暴力解法:

首先看这个数据量,就直接去写暴力了…
骗分过样例,暴力出奇迹,爆搜挂着机,打表出省一
这题暴力能过。
就拿样例的第一个数据来说:
首先,我们把 n n n m m m m m m n n n列出来:

a[]:5 5 5 5
b[]:4 4 4 4 4

然后做如下操作:
1 1 1.我们比较一下两个数列重合部分的大小,发现下面的数列较小,我们就把他们放到 a n s ans ans数组中:

a[]:5 5 5 5
b[]:4 4 4 4 4
ans[]:4 4 4 4

2 2 2.然后,我们再将a和b数组里的重合部分减去,得到:

a[]:1 1 1 1
b[]:0 0 0 0 4
ans[]:4 4 4 4

3 3 3.因为每次都会有一个数组前面都是0,所以将0都后置:

a[]:1 1 1 1
b[]:4 0 0 0 0
ans[]:4 4 4 4

重复步骤 123 123 123,最后就会得到:

a[]:0 0 0 0
b[]:0 0 0 0 0
ans[]:4 4 4 4 1 1 1 1//答案

这样我们就得到了字典序最大组数最少的方法啦~~~~~
(我也不知道为什么,有没有dalao来给蒟蒻解释一下…)

A C AC AC C o d e ( Code( Code(暴力 ) ) )

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int t,n,m,ans1[MAXN],minn,maxn,ans2[MAXN],sum[MAXN],now,ans,dx,dy;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        if(n<m) swap(n,m);
        now=ans=0;
        for(int i=1;i<=n;i++) ans1[i]=m;
        for(int i=1;i<=m;i++) ans2[i]=n;//初始化,分别表示n个m和m个n
        while(m&&n){
            dx=dy=0;//表示两个数组中0的个数
            for(int i=1;i<=min(m,n);++i){
                now=min(ans1[i],ans2[i]);
                sum[++ans]=now;//储存答案
                ans1[i]-=now;
                ans2[i]-=now;//减去较小的
                if(!ans1[i]) ++dx;
                else ++dy;//记录数组中前导0的数量
            }
            for(int i=dx+1,j=0;i<=n;++i)ans1[++j]=ans1[i];
            for(int i=dy+1,j=0;i<=m;++i)ans2[++j]=ans2[i];//每次都将0后置
            n-=dx;m-=dy;//更新两个数组尾指针的
        }
		if(n)
			for(int i=1;i<=n;++i)
            	if(ans1[i]) sum[++ans]=ans1[i];
        else if(m)
			for(int i=1;i<=m;++i)
            	if(ans2[i]) sum[++ans]=ans2[i];//如果数组中还有剩余元素就放进答案
        printf("%d",ans);
        puts("");
        for(int i=1;i<=ans;++i){
            printf("%d ",sum[i]);
            sum[i]=0;//输出的同时清零比memset或另外清快
        }
        puts("");
    }
}

正解:

首先发现只要 g c d ( n , m ) ! = 1 gcd(n,m)!=1 gcd(n,m)!=1,结果就是 n / g c d ( n , m ) n/gcd(n,m) n/gcd(n,m) m / g c d ( n , m ) m/gcd(n,m) m/gcd(n,m),随后我们就可以用类似于辗转相除法的思想来分配。
做法:每次都装 m i n ( n , m ) min(n,m) min(n,m)个口罩,剩下口罩的数量为: ( m a x ( n , m ) − m i n ( n , m ) ) ∗ m i n ( n , m ) (max(n,m)-min(n,m))*min(n,m) (max(n,m)min(n,m))min(n,m)随后只要重复上述过程就可以了。

A C AC AC C o d e ( Code( Code(正解 ) ) ):

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int t,n,m,a[MAXN],ans;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        ans=0;//组数
        while(n>0){
            if(n<m) swap(n,m);
            for(int i=1;i<=m;++i)
                a[++ans]=m;
            n-=m;
        }
        printf("%d",ans);
        puts("");
        for(int i=1;i<=ans;++i)
            printf("%d ",a[i]);
        puts("");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值