C语言程序设计100例之(37):文具订购
例37 文具订购
题目描述
小明的班上共有n元班费,同学们准备使用班费集体购买 3 种物品:
圆规,每个7元。
笔,每支4元。
笔记本,每本3元。
小明负责订购文具,设圆规,笔,笔记本的订购数量分别为a,b,c,他订购的原则依次如下:
n 元钱必须正好用光,即 7a+4b+3c=n。
在满足以上条件情况下,成套的数量尽可能大,即 a,b,c中的最小值尽可能大。
在满足以上条件情况下,物品的总数尽可能大,即 a+b+c尽可能大。
请你帮助小明求出满足条件的最优方案。可以证明若存在方案,则最优方案唯一。
输入格式
输入仅一行一个整数,代表班费数量 n(0≤n≤105)。
输出格式
如果问题无解,请输出−1。
否则输出一行三个用空格隔开的整数 a, b, c,分别代表圆规、笔、笔记本的个数。
输入样例
33
输出样例
1 2 6
说明/提示
样例解释
a=2,b=4,c=1也是满足条件 1,2的方案,但对于条件 3,该方案只买了 7 个物品,不如 a=1,b=2,c=6的方案。
(1)编程思路。
因为全套的价格是14元,因此要使a、b、c中的最小值尽可能大,购买时应尽可能地买更多的整14元。而全套中7元最大,7=3+4,也就是一个a可以被拆成一个b和一个c,因此a应该是a、b、c中的最小值,最大可能为n/14。
在购买了尽可能多的整14元之后,剩余的钱再尽量拆分成3元或4元。如果买完尽量多的整14元套数后,剩下的钱不能拆分为3元或4元的组合,只需要少购买一整套14元的物品(a=a-1),相当于剩下的前多了14元,一定可以分解为3元和4元的组合。
为了尽量买更多的物品(即a+b+c尽可能大),剩下的钱应该优先购买c。例如12可分拆为3个b,或者4个c。
有了上面的分析,可以对a、b、c的取值进行穷举。
先穷举最小值a的个数,其取值范围最大为n/14,最小为0,从n/14穷举到0。
再穷举c,其穷举范围的最大值为(n-7*a)/3,最小值为a,同样从最大值(n-7*a)/3穷举到最小值a。
剩下的b无需穷举,直接计算b=n-(7*a+3*c);,如果b满足条件
(b>=0 && b%4==0 && b/4>=a)
则b/4的值就是所求的b。找到了一组a、b、c的值后,它一定是最优方案,直接输出并退出程序。
(2)源程序。
#include <stdio.h>
int main( )
{
int n;
scanf("%d",&n);
if (n==0)
{
printf("0 0 0\n");
return 0;
}
int a,b,c;
for (a=n/14;a>=0;a--)
{
for (c=(n-7*a)/3;c>=a;c--)
{
b=n-(7*a+3*c);
if (b>=0 && b%4==0 && b/4>=a)
{
printf("%d %d %d\n",a,b/4,c);
return 0;
}
}
}
printf("-1\n");
return 0;
}
习题37
37-1 糖果包装盒
题目描述
Mirko 需要向某地的一家糖果店运送 n 颗糖。
Mirko 可以使用两种类型的包装盒子:
一种是可以装 3 颗糖的 1 号包装;
另一种是可以装 5 颗糖的 2 号包装。
Mirko 想让包装盒尽可能少。例如,他要运送18颗糖,则可以使用6个1号包装。但是,最优策略是3个2号包装和1个1号包装。这样总共有 4 个包装。
请你帮助 Mirko 找到需要包装盒最少的方案。
输入格式
输入数据共一行。
第一行一个正整数 n(3≤n≤5000),表示糖果颗数。
输出格式
输出数据共一行。
第一行,一个正整数表示需要包装盒最少的方案数的包装盒数,如果不可能用这 2 种包装盒运 n 颗糖,输出 -1。
输入样例
18
输出样例
4
(1)编程思路。
对1号包装的数量x(0≤x≤n/3)进行穷举,找出最小的包装盒数。
(2)源程序。
#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
int x,y;
int ans=5000,flag=0;
for (x=0;x<=n/3;x++)
{
y=(n-3*x)/5;
if (3*x+5*y==n)
{
flag=1;
if (x+y<ans) ans=x+y;
}
}
if (flag==1) printf("%d\n",ans);
else printf("-1\n");
return 0;
}
37-2 号码锁
本题选自洛谷题库 (https://www.luogu.org/problem/P2693)
题目描述
农夫约翰的奶牛不停地从他的农场中逃出来,导致了很多损害。为了防止它们再逃出来,他买了一只很大的号码锁以防止奶牛们打开牧场的门。
农夫约翰知道他的奶牛很聪明,所以他希望确保它们不会在简单地试了很多不同的号码组合之后就能轻易开锁。锁上有三个转盘,每个上面有数字 1 ~ n,因为转盘是圆的,所以 1 和 n 是相邻的。有两种能开锁的号码组合,一种是农夫约翰设定的,还有一种“预设”号码组合是锁匠设定的。但是,锁有一定的容错性,所以,在每个转盘上的数字都与一个合法的号码组合中相应的数字相距两个位置以内时,锁也会打开。
比如说,如果农夫约翰的号码组合是 ( 1 , 2 , 3 ),预设号码组合是 ( 4 , 5 , 6 ),在转盘被设定为 ( 1 , 4 , 5)(因为这和农夫约翰的号码组合足够接近)或 ( 2 , 4 , 8 )(因为这和预设号码组合足够接近)时可以打开锁。注意,( 1 , 5 , 6 )并不会打开锁,因为它与任一号码组合都不够接近。
给出农夫约翰的号码组合和预设号码组合,请计算能够开锁的不同的号码组合的数目。号码是有序的,所以 ( 1 , 2, 3 ) 与 ( 3 , 2 , 1 ) 不同。
输入格式
输入的第一行是一个整数 n(1≤n≤100),代表锁上的数字个数。
输入的第二行有三个整数 x, y, z,代表农夫约翰的号码组合。
输入的第三行有三个整数 a, b, c,代表预设的号码组合(1≤x,y,z,a,b,c≤n)。
输出格式
输出一行一个整数代表能够开锁的组合数目。
输入样例
50
1 2 3
5 6 7
输出样例
249
(1)编程思路。
对三个数字111~nnn进行穷举即可。
(2)源程序。
#include <stdio.h>
int abs(int a)
{
return a>=0?a:-a;
}
int judge(int n1,int n2,int n3,int c1,int c2, int c3,int N)
{
if (abs(n1-c1)>2 && abs(n1-c1)<N-2 ) return 0;
if (abs(n2-c2)>2 && abs(n2-c2)<N-2 ) return 0;
if (abs(n3-c3)>2 && abs(n3-c3)<N-2 ) return 0;
return 1;
}
int main(void)
{
int n;
scanf("%d",&n);
int x,y,z;
int a,b,c;
scanf("%d%d%d",&x,&y,&z);
scanf("%d%d%d",&a,&b,&c);
int ans=0;
int i,j,k;
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
for (k=1; k<=n; k++)
if (judge(i,j,k,x,y,z,n)||judge(i,j,k,a,b,c,n))
ans++;
printf("%d\n",ans);
return 0;
}
37-3 饲料调配
本题选自洛谷题库 (https://www.luogu.org/problem/P2729)
题目描述
农夫约翰从来只用调配得最好的饲料来喂他的奶牛。饲料用三种原料调配成:大麦,燕麦和小麦。他知道自己的饲料精确的配比,在市场上是买不到这样的饲料的。他只好购买其他三种混合饲料(同样都由三种麦子组成),然后将它们混合,来调配他的完美饲料。
给出三组整数,表示 大麦:燕麦:小麦 的比例,找出用这三种饲料调配 x:y:z 的饲料的方法。
例如,给出目标饲料 3:4:5 和三种饲料的比例:
1:2:3 3:7:1 2:1:2 你必须编程找出使这三种饲料用量最少的方案,要是不能用这三种饲料调配目标饲料,输出“NONE”。“用量最少”意味着三种饲料的用量(整数)的和必须最小。
对于上面的例子,你可以用8份饲料1,1份饲料2,和5份饲料3,来得到7份目标饲料:
8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5)
表示饲料比例的整数以及目标饲料的都是小于100的非负整数。表示各种饲料的份数的整数,都小于100。一种混合物的比例不会由其他混合物的比例直接相加得到。
输入格式
Line 1: 三个用空格分开的整数,表示目标饲料
Line 2..4: 每行包括三个用空格分开的整数,表示农夫约翰买进的饲料的比例
输出格式
输出文件要包括一行,这一行要么有四个整数,要么是“NONE”。前三个整数表示三种饲料的份数,用这样的配比可以得到目标饲料。第四个整数表示混合三种饲料后得到的目标饲料的份数。
输入样例
3 4 5
1 2 3
3 7 1
2 1 2
输出样例
8 1 5 7
(1)编程思路。
数据范围较小,直接穷举即可。
(2)源程序。
#include <stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
int main(void)
{
int a[5],b[5],c[5],num;
int i,j,k;
for (i=0;i<4;++i)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for (i=0;i<100;i++)
for (j=0;j<100;j++)
for (k=0;k<100;k++)
{
a[4]=i*a[1]+j*a[2]+k*a[3];
b[4]=i*b[1]+j*b[2]+k*b[3];
c[4]=i*c[1]+j*c[2]+k*c[3];
num=0;
if (a[0]!=0)
num=max(num,a[4]/a[0]);
if (b[0]!=0)
num=max(num,b[4]/b[0]);
if (c[0]!=0)
num=max(num,c[4]/c[0]);
if (num!=0 && a[0]*num==a[4] && b[0]*num==b[4] && c[0]*num==c[4])
{
printf("%d %d %d %d\n",i,j,k,num);
return 0;
}
}
printf("NONE\n");
return 0;
}