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

Mask Allocation

原题请看这里

题目描述:

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

输入描述:

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

输出描述:

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

样例输入:

2
5 4
3 3

样例输出:

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

思路:

暴力解法:

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

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

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

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

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

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

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

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

重复步骤 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 C o d e ( 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 ,结果就是 n / g c d ( n , m ) n/gcd(n,m) m / g c d ( n , m ) m/gcd(n,m) ,随后我们就可以用类似于辗转相除法的思想来分配。
做法:每次都装 m i n ( 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) 随后只要重复上述过程就可以了。

A C AC C o d e ( 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("");
    }
}

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107735090