例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;
}