以下案例选自
《第一行代码 C语言》 人民邮电出版社 主编:翁惠玉
对部分案例添加了自己的代码。
1.枚举法:
- 对可能的结果值按某种一定顺序一个一个的枚举检验
- 适合解的数量有限,可枚举的场合,特点是直观,但时间性能差,注意优化,提高效率。
一、ABCD x E =DCBA(A,B,C,D代表不同的数字)
分析:
1)A和D不能位0
2)因为A,B,C,D代表不同的数字,E不能位1
方案一:
#include <stdio.h>
int main()
{
int A,B,C,D,E,num1,num2;
for(A = 2;A <= 9; ++A){
//printf(" A=%d \n",A);
for(B = 0;B <= 1;++B){
if(A == B) continue;
//printf(" B=%d \n",B);
for(C = 6;C <= 7;++C){
if(C==A||C == B ) continue;
//printf(" C=%d \n",C);
for(D = 1;D <= 9;++D){
if( D == A|| D == B || D == C) continue;
//printf(" D=%d \n",D);
for(E = 2;E <= 9;++E){
if(E == A || E == B||E == C||E == D ) continue;
//printf(" E=%d \t",E);
num1 = A*1000+B*100+C*10+D;
num2 = D*1000+C*100+B*10+A;
if(num1 * E == num2)
printf(" %d * %d =%d\n",num1,E,num2);
}
}
}
}
}
return 0;
}
方案二(改进版):
#include <stdio.h>
int main()
{
int A,B,C,D,E,num1,num2;
for(num1 = 1023;num1 <= 9876; ++num1){ /*枚举每个可能的4位数*/
A = num1 / 1000; /*取出每个数字的A、B、C、D*/
B = num1 % 1000 / 100;
C = num1 %100 / 10;
D = num1 %10;
if(D == 0 || A == B || A == C || A == D || B == C || B == D || C == D)
continue;
num2 = D * 1000 + C * 100 + B * 10 + A; /*构造num2*/
for (E = 2;E <= 9; ++E ){ /*检查每一个可能的*/
if (E == A || E == B || E == C || E ==D) continue;
if (num1 * E == num2)
printf("%d * %d = %d\n",num1,E,num2);
}
}
return 0;
}
结果:
反思总结:
二、阶梯问题
有一个长阶梯
若每步上2阶,最后剩下1阶,若每步上3阶,最后剩下2阶
若每步上5阶,最后剩下4阶,若每步上6阶,最后剩下5阶
只有每步上7阶,最后刚好一阶也不剩下
请问该阶梯至少有多少阶?
方案一:(自己实现):
int i=1;
int f =1;
while(f)
{
if( i % 2 ==1 && i % 3 == 2 && i % 5 == 4 && i % 6 == 5 && i % 7 == 0 )
{
f = 0;
printf("这个数至少为:%d",i);
}
i++;
}
方案二:课本解答
int n;
for (n = 7; ; n += 7)//至少从7开始,且是7的倍数
if ( n % 2 ==1 && n % 3 == 2 && n % 5 == 4 && n % 6 == 5 && n % 7 == 0 ) break;//break是循环唯一的出口
printf("满足的阶梯长度:%d\n",n);
结果:
反思总结:
三、水果问题
用150元买了3种水果,各种水果加起来一共100个,西瓜10元一个,苹果3元一个,橘子1元一个,设计一个程序,输出每种水果各买了几个。
分析:
两个约束条件:
1)3种水果共100个
2)买了3种水果一共花了150元
方案一:(自己实现):
int numx,nump,numj;
for(numx = 1; numx <= 15; numx++ ){
for(nump = 1; nump <= 50; nump++ ){
for(numj = 1; numj <= 150; numj++ ){
if((numx + nump + numj) = = 100 && (10*numx + 3*nump + numj) == 150){
n++;
printf("西瓜的个数:%d\t苹果的个数:%d\t橘子的个数:%d\n",numx,nump,numj);
}
}
}
}
方案二:课本解答
int numx,nump,numj;
for(numx = 1; numx <= 15; numx++ )
for(nump = 1; nump < 150 - 10 * numx; nump++ ){
numj = 150-10*numx-3*nump;
if(numx+nump+numj == 100){
printf("西瓜的个数:%d\t苹果的个数:%d\t橘子的个数:%d\n",numx,nump,numj);
}
}
结果:
反思总结:
按一个约束条件列出所有可行的情况,然后对每个可能的解检查它是否满足另一个约束条件。
例如:按照第二个约束条件,至少必须买一个西瓜,至多买14个西瓜,因此可能的西瓜数的变化范围是1~14.当西瓜数确定后,剩下的钱是(150 - 10 x 西瓜数),可以买苹果橘子。苹果至少必须买一个,至多买(150 - 10 x 西瓜数 - 1)个苹果。剩下的钱买橘子,一共可以买(150 - 10 x 西瓜数 - 3 x 苹果数)个橘子。对此方案检查是否满足第一个约束条件。如果满足,则输出。
四、求最长连续子序列以及和值
给定(可能是负的)整数序列 A1,A2,A3…寻找(Ak+ Ak+1 + … Aj)值为最大的序列。如果所有的整数都是负的,那么最长连续子序列的和是0。
要求:
设计一个函数找出给定序列中最长连续子序列以及和值。
方案一::
#include <stdio.h>
int maxSubsequnceSum( int a[], int size, int *start, int *end );
int main()
{
int a[] = {-2, 11, -4, 13, -5, 2};
int b[] = {1, -3, 4, -2, -1, 6};
int sum,start,end;
sum = maxSubsequnceSum(a, 6, &start, &end );
printf("{-2, 11, -4, 13, -5, 2}中的最大和值从 %d 开始到 %d 结束,和值是 %d\n",start,end,sum);
sum = maxSubsequnceSum(b, 6, &start, &end );
printf("{1, -3, 4, -2, -1, 6}中的最大和值从 %d 开始到 %d 结束,和值是 %d\n",start,end,sum);
return 0;
}
int maxSubsequnceSum( int a[], int size, int *start, int *end )
{
int maxSum = 0; /*当前的最大子序列和*/
int i, j, k;
for (i = 0; i < size; i++){ /*子序列的起始位置*/
for (j = i; j<size; j++){ /* 子序列的终止位置*/
int thisSum = 0;
for (k = i; k <= j; k++) /*从i开始到j结束的序列和*/
thisSum += a[k];
if( thisSum > maxSum){ /* 找到一个更好的序列*/
maxSum = thisSum;
*start = i;
*end = j;
}
}
}
return maxSum;
}
方案二:(改进版)
int maxSubsequnceSum( int a[], int size, int *start, int *end )
{
int maxSum = 0; /*当前的最大子序列和*/
int i, j;
for (i = 0; i < size; i++){ /*子序列的起始位置*/
int thisSum = 0; /*从i开始的序列和*/
for (j = i; j<size; j++){ /* 子序列的终止位置*/
thisSum += a[j]; /*计算从i开始到j结束的序列和*/
if( thisSum > maxSum){ /* 找到一个更好的序列*/
maxSum = thisSum;
*start = i;
*end = j;
}
}
}
return maxSum;
}
结果:
反思总结:
第一种方案简单,使用3次for循环,逐一进行枚举。但执行效率不高,时间复杂度高。
第二种方案使用两次for循环,使得在进行新的序列计算时使用上一次的和值,减少运算量。
收获:
1)使用数组进行位置的定位
2)编写算法时注意是否利用已存在的条件,直接使用,提高算法的运算效率
2.贪婪法
此问题的解决过程由一系列阶段组成,贪婪法在求解过程的每一个阶段都选取一个阶段都选取的一个在该阶段看似最优的解,把每一个阶段的结果合并起来形成一个全局解。
1、硬币找零问题
#include <stdio.h>
#define ONEFEN 1
#define TWOFEN 2
#define FIVEFEN 5
#define ONEJIAO 10
int main()
{
int money;
int onefen = 0,twofen = 0,fivefen = 0,onejiao = 0;
printf("输入要找零的钱?(以分为单位):");
scanf("%d",&money);
/*从大到小不断尝试每一种硬币*/
if (money >= ONEJIAO){
onejiao = money / ONEJIAO;
money %= ONEJIAO;
}
if (money >= FIVEFEN){
fivefen = 1;
money -= FIVEFEN;
}
if (money >= TWOFEN){
twofen = money / TWOFEN;
money %= TWOFEN;
}
if (money >= ONEFEN) onefen = 1;
/*输出结果*/
printf("1角硬币币数:%d\n",onejiao);
printf("5分硬币币数:%d\n",fivefen);
printf("2分币币数:%d\n",twofen);
printf("1分硬币币数:%d\n",onefen);
return 0;
}
2、找出由 5、6、2、9、4、1中的3个数字组成的最大3位数的程序
#include <studio. h>
int main()
{
int num = 0,max = 10,current,n,digit;
int a[6] = {5,6,2,4,9,1};
for(digit = 100;digit >0;digit /=10){
current = 0;
for (n=0;n<6;++n){
if (a[n] > current && a[n] <max)
current = a[n];
}
num += digit * current;
max = current;
}
printf("%d\n",num);
return 0;
}