Mask Allocation
题目描述:
有
n
∗
m
n*m
n∗m个口罩需要分配。已知共有
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(1≤T≤100),表示测试用例的数量。 对于每个测试用例,输入的唯一行包含两个整数 n , m ( 1 ≤ n , m ≤ 1 0 4 ) n,m(1 \leq n,m \leq 10 ^ 4) n,m(1≤n,m≤104),代表重症医院和轻症医院的数量。
输出描述:
对于每个测试用例,请输出两行。 第一行应在第一行中包含一个整数 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("");
}
}