2020 NOI online 入门组第一题题解--zhengjun
题目描述
小明的班上共有 \(n\) 元班费,同学们准备使用班费集体购买 \(3\) 种物品:
圆规,每个 \(7\) 元。
笔,每支 \(4\) 元。
笔记本,每本 \(3\) 元。
小明负责订购文具,设圆规,笔,笔记本的订购数量分别为 \(a,b,c\),他订购的原则依次如下:
- \(n\) 元钱必须正好用光,即 \(7a+4b+3c=n\)。
- 在满足以上条件情况下,成套的数量尽可能大,即 \(a,b,c\) 中的最小值尽可能大。
- 在满足以上条件情况下,物品的总数尽可能大,即 \(a+b+c\) 尽可能大。
请你帮助小明求出满足条件的最优方案。可以证明若存在方案,则最优方案唯一。
输入格式
输入仅一行一个整数,代表班费数量 \(n\)。
输出格式
如果问题无解,请输出 \(-1\)。
否则输出一行三个用空格隔开的整数 \(a, b, c\),分别代表圆规、笔、笔记本的个数。
输入输出样例
输入 #1 复制
1
输出 #1 复制
-1
输入 #2 复制
14
输出 #2 复制
1 1 1
输入 #3 复制
33
输出 #3 复制
1 2 6
说明/提示
样例输入输出 3 解释
\(a=2,b=4,c=1\) 也是满足条件 \(1,2\) 的方案,但对于条件 \(3\),该方案只买了 \(7\) 个物品,不如 \(a=1,b=2,c=6\) 的方案。
数据规模与约定
- 对于测试点 \(1 \sim 6\),保证 \(n \leq 14\)。
- 对于测试点 \(7 \sim 12\),保证 \(n\) 是 \(14\) 的倍数。
- 对于测试点 \(13 \sim 18\),保证 \(n \leq 100\)。
- 对于 \(100\%\) 的数据,保证 \(0 \leq n \leq 10^5\) 。
思路
这么简单的一道题,考试时竟然没有注意到一个细(kēng)节(diǎn),实在是失策失策
好了,进入正题
说白了,就是求一元三次方程的非负整数解(其他的特殊要求先不管)
首先,我们要先判断 \(7a+4b+3c=n\) 是否可以有非负整数解。
我们发现这个方程中 : \(a\) 的系数就等于 \(b\) 的系数加 \(c\) 的系数,即 \(7=3+4\)
所以,要判断是否有非负整数解,只要判断二元一次方程 \(3x+4y=n\) 是否有非负整数解就可以了。
然后我们发现 \(gcd(3,4)=1\) \((\ 3,4\) 的最大公因数是 \(1\ )\), 也就是说,只要 \(n\) 大于 \(7\) 并且 \(n\) 是 \(1\) 的倍数,这个方程就一定有非负整数解。
例如 \(9x+12y=n\),\(gcd(9,12)=3\),所以,只要 \(n\) 大于 \(21\) 并且是 \(3\) 的倍数,那么这个方程就一定有非负整数解
你还可以自己去举更多的例子。
这样,我们就可以判断 \(7x+4b+3c=n\) 这个方程是否有非负整数解了( \(0\) 到 \(7\) 的自己可以算(còu),结果就是只有 \(1\),\(2\),\(5\) 没有非负整数解)。
然后,就是整道题目的核心
先说明,我讲的不是暴力,是分类讨论(加上一点数形结合、方程以及类比的思想)
分类讨论:关于 \(n\ mod\ 7\) 的分类 ( 以下的分类都是在由负整数解的情况下所述的 )
比如说这幅图。
设 \(n=7k+x\ (\ 0\leq x\lt7\ )\)
- 当 \(n\ mod\ 7=0\) 时,最优的方案就应该是把 \(k\) 分成两份,一份分给 \(7\) ,另一份分给 \(3,4\),如果不可以平均分,那么为了让买的总数最大,就应该把剩下的 \(1\) 个 \(7\) 给 \(3,4\) 。
- 当 \(n\ mod\ 7=1\) 时,因为 \(3\) 和 \(4\) 无法拼出 \(1\) ,所以我们要拆开一个 \(7\) ,拿出来和 \(1\) 一起用两个 \(4\) 拼出,但是,如果可以拆出来两个 \(7\) 的话,那么就可以拼出五个 \(3\) ,却少拼了一个 \(3\) 和一个 \(4\) ,还是比前一种方案优,所以,只要可以拆出来两个 \(7\) ,就要分给 \(3\) ,否则就拆一个出来分给 \(4\) 。
- 当 \(n\ mod\ 7=2\) 时,就要拿出一个 \(7\) 拼三个 \(3\) ,如果拆两个的话,可以算一下,没有拆一个的方案优。
- 当 \(n\ mod\ 7=3\) 时,那 \(x\) 就直接给 \(3\) 。
- 当 \(n\ mod\ 7=4\) 时,\(x\) 就直接给 \(4\) 。
- 当 \(n\ mod\ 7=5\) 时,就要加一个 \(7\) ,得到 \(12\),那么为了让买的总数最多,应该把这 \(12\) 都给 \(3\) 是最优的。
- 当 \(n\ mod\ 7=6\) 时,\(x\) 就都给 \(3\) 就可以了
所以公式就是:
提示:一下所述的除法都是整除
- 当 \(n\ mod\ 7=0\) 时,\(a=b=c=n\div7\div2=n\div14\)
- 当 \(n\ mod\ 7=1\) 时,如果 \(n\le8\) 时 \(a=(n\div7-1)\div2\ ,\ b=n\div7-a-1+2=n\div7-a+1\ ,\ c=n\div7-a-1\),如果 \(n>8\) 时 \(a=(n\div7-2)\div2=n\div14-1\ ,\ b=n\div7-a-2+2=n\div7-a\ ,\ c=n\div7-a-2\)
- 当 \(n\ mod\ 7=2\) 时,\(a=(n\div7-1)\div2\ ,\ b=n\div7-a-1\ ,\ c=n\div7-a-1+3=n\div7-a+2\)
- 当 \(n\ mod\ 7=3\) 时,\(a=b=n\div7\div2=n\div14\ ,\ c=n\div7\div2+1=n\div14+1\)
- 当 \(n\ mod\ 7=4\) 时,\(a=c=n\div7\div2\ ,\ b=n\div7\div2+1\)
- 当 \(n\ mod\ 7=5\) 时,\(a=(n\div7-1)\div2\ ,\ b=n\div7-a-1\ ,\ c=n\div7-a-1+4=n\div7-a+3\)
- 当 \(n\ mod\ 7=6\) 时,\(a=b=n\div7\div2\ ,\ c=n\div7\div2+2\)
可能有点看不太清,换表格
x的值 | a的值 | b的值 | c的值 |
---|---|---|---|
n%7=0 | n÷14 | n÷7-n÷14 | n÷7-n÷14 |
n%7=1 | (n÷7-1)÷2 | n÷7-(n÷7-1)÷2+1 | n÷7-(n÷7-1)÷2-1 |
n%7=2,n≤8 | (n÷7-1)÷2 | n÷7-(n÷7-1)÷2-1 | n÷7-(n÷7-1)÷2 |
n%7=2,n>8 | n÷14-1 | n÷7-n÷14-1 | n÷7-n÷14+4 |
n%7=3 | n÷14 | n÷7-n÷14 | n÷7-n÷14+1 |
n%7=4 | n÷14 | n÷7-n÷14+1 | n÷7-n÷14 |
n%7=5 | (n÷7-1)÷2 | n÷7-(n÷7-1)÷2-1 | n÷7-(n÷7-1)÷2+3 |
n%7=6 | n÷14 | n÷7-n÷14 | n÷7-n÷14+2 |
注意,在所有的 \(b,c\) 中的 \(n\div7-n\div14\) 绝对不可以改成 \(n\div14\) ,因为是整除
好了,不多说了,细(kēng)节(diǎn)不少
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
scanf("%d",&n);
if(n==1||n==2||n==5)
printf("-1");
else if(n%7==0)
printf("%d %d %d",n/14,n/7-n/14,n/7-n/14);
else if(n%7==1)
if(n>8)
printf("%d %d %d",n/14-1,n/7-n/14-1,n/7-n/14+4);
else
printf("%d %d %d",(n/7-1)/2,n/7-(n/7-1)/2+1,n/7-(n/7-1)/2-1);
else if(n%7==2)
printf("%d %d %d",(n/7-1)/2,n/7-(n/7-1)/2-1,n/7-(n/7-1)/2+2);
else if(n%7==3)
printf("%d %d %d",n/14,n/14,n/14+1);
else if(n%7==4)
printf("%d %d %d",n/14,n/7-n/14+1,n/7-n/14);
else if(n%7==5)
printf("%d %d %d",(n/7-1)/2,n/7-(n/7-1)/2-1,n/7-(n/7-1)/2+3);
else if(n%7==6)
printf("%d %d %d",n/14,n/7-n/14,n/7-n/14+2);
return 0;
}