C语言算法练习-中级-(通用设计思想)

以下案例选自
《第一行代码 C语言》 人民邮电出版社 主编:翁惠玉
对部分案例添加了自己的代码。

通用算法设计

(通用算法设计)

1.枚举法:

  1. 对可能的结果值按某种一定顺序一个一个的枚举检验
  2. 适合解的数量有限,可枚举的场合,特点是直观,但时间性能差,注意优化,提高效率。

一、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;
}

结果:在这里插入图片描述
反思总结:

在循环体中,首先检查4位数的4个数字是否相同。如果相同,就放弃该数字,如果4个数字都不相同,即将该4位数字颠倒。最后对每一个可能的E检查是否成立。

二、阶梯问题
有一个长阶梯
若每步上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);

结果:
在这里插入图片描述
反思总结:

因正好能被7整除,所以只要枚举从7开始,能被7整除的数即可。

三、水果问题
用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;



} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值