例16  巧解算式

问题描述

在1、2、3、4、5、6、7、8、9、10个数中间加上加号或减号,使得到的表达式的值为自然数N,如果中间没有符号,则认为前后为一个数,如1 2 3认为是一百二十三(123)。

例如:当N=100时,表达式值为100的填法有24种。123+4+5+67-89-10=100是一种填法,1+2+3+4+56+7+8+9+10=100也是一种填法。

编写一个程序,找出使表达式的值等于N的填写方法有多少种?

输入格式

输入包含多组测试数据。每组测试数据一个自然数n,占据独立一行。0表示输入结束。

输出格式

对每组测试数据,输出一行,即使表达式的值等于n的填写方法的种数。

输入样例

1

10

100

0

输出样例

45

26

24

(1)编程思路。

为了表示等式左边的算式,可以定义一个数组int a[20],其中元素a[0]、a[2]、…、a[16]分别保存数字1、2、…、9,a[18]和a[19]合起来保存数10。a[1]、a[3]、…、a[17]用0、1、2保存可能填写的运算符,其中0代表空格、1代表加号+、2代表减号 -。如下所示:

1

2

3

4

5

6

7

8

9

1

0

这样,可以用一个9重循环对9个空位可能填写的3种算符进行穷举,得到等式左边的算式,保存在数组a[20]中,然后对这个算式进行解析,若运算结果为N,则就是一种解法。

程序中将算式的解析编写成一个函数,原型为int Parse(int a[]); 。

解析时,从左到右对数组进行扫描。初始时,算式值value=0,应进行运算的算符preCalc=1(这样当扫描到第一个加或减运算符时,value(0)加上算符前的数值就是第1个算符前的数,从而可进行循环处理),当前参与运算的数preVal初始为a[0]。

用循环对a[1]、a[3]、…、a[17]这9个位置填写情况进行处理,分两种情况:

1)填写的是空格,则前面的数(保存在preVal中)和后面的一个数字构成一个数,如1和2之间是空格,则1和2构成12,若“2”后面又是一个空格,则“12”和“3”构成123。处理方法为  preVal=preVal*10+a[i+1];

2)填写的是加号或减号。则进行前面的运算(保存在应进行运算的算符preCalc中,注意不是当前的加法或减法运算,当前的加号或减号的运算需等到扫描到下一个加或减运算符时才进行)。若preCale==1,则进行加法,若preCale==2,则进行减法。之后,将当前的算符保存到preCale中,并将算符后的一个数字保存到参与运算的数preVal中。

if (preCalc==1)

value=value+preVal;

else

value=value-preVal;

preCalc=a[i];

preVal=a[i+1];

(2)源程序。

#include <stdio.h>

int Parse(int a[]);

int main()

{

int a[20]={1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,1,0};

// 数组a中a[1]、a[3]、…、a[17]用0、1、2保存可能的运算符

// 其中,0代表空格,1代表加号+、2代表减号-

int n;

while (scanf("%d",&n) && n!=0)

{

int a1,a2,a3,a4,a5,a6,a7,a8,a9,num=0;

for(a1=0;a1<3;a1++)

for(a2=0;a2<3;a2++)

for(a3=0;a3<3;a3++)

for(a4=0;a4<3;a4++)

for(a5=0;a5<3;a5++)

for(a6=0;a6<3;a6++)

for(a7=0;a7<3;a7++)

for(a8=0;a8<3;a8++)

for(a9=0;a9<3;a9++)

{

a[1]=a1;            a[3]=a2;            a[5]=a3;

a[7]=a4;            a[9]=a5;            a[11]=a6;

a[13]=a7;          a[15]=a8;          a[17]=a9;

if (Parse(a)==n)

{

num++;

}

}

printf("%d\n",num);

}

return 0;

}

int Parse(int a[])

{

int value=0,i,preVal,preCalc;

preVal=a[0]; preCalc=1;

for(i=1;i<=17;i=i+2)

{

switch (a[i])

{

case 0: preVal=preVal*10+a[i+1];

break;

case 1:

case 2: if (preCalc==1)

value=value+preVal;

else

value=value-preVal;

preCalc=a[i];

preVal=a[i+1];

}

}

preVal=preVal*10+a[19];

if (preCalc==1)

value=value+preVal;

else

value=value-preVal;

return value;

}

习题16

16-1  零的数列 Zero Sum

本题选自洛谷题库(https://www.luogu.org/problem/P1473)

题目描述

考虑一个由1到N(N=3, 4, 5 ... 9)的数字组成的递增数列:1 2 3 ... N。 现在在数列中插入“+”表示加,或者“-”表示减,“ ”表示空白(例如1-2 3就等于1-23),来将每一对数字组合在一起(请不要在第一个数字前插入符号)。 计算该表达式的结果并判断其值是否为0。 请你写一个程序找出所有产生和为零的长度为N的数列。

输入格式

单独的一行表示整数N (3 <= N <= 9)。

输出格式

按照ASCII码的顺序,输出所有在每对数字间插入“+”, “-”, 或 “ ”后能得到结果为零的数列。

输入样例

7

输出样例

1+2-3+4-5-6+7

1+2-3-4+5+6-7

1-2 3+4+5+6+7

1-2 3-4 5+6 7

1-2+3+4-5+6-7

1-2-3-4-5+6+7

(1)编程思路。

弄懂了例16的编程思路,适当变化可以解决本习题。

同样定义数组 int a[17]={1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9};

数组a中,a[1]、a[3]、…、a[15]用0、1、2保存可能的运算符。其中,0代表空格、1代表加号+、2代表减号-。

本题关键是解决如何给a[1]、a[3]、…a[2*n-3]这n-1个元素赋以0、1、2这三种算符构成的全排列。

以n=3为例,应该在a[1]和a[3]处赋两种算符。全排列为:00、01、02、10、11、12、20、21、22共9种。

编写如下的递归函数可以生成在0、1、2中任取n-1个数字构成的全排列(可重复取某个数字),并赋给相应的a[1]、a[3]、…a[2*n-3]这n-1个元素。

void dfs(int a[], int pos,int n)

{

if(pos == 2*n-1)     // 已赋了N-1个位置的算符

{

// 对生成的表达式进行解析,看结果是否为0,若为0,输出该表达式

return;

}

for(int i = 0;i <= 2;i++)

{

a[pos] = i;

dfs(a,pos+2,n);   // 给下一位置赋算符

}

}

表达式生成后,其解析方式也如同例16的思路,只是本题中解析到a[2*n-2]为止。

(2)源程序。

#include <stdio.h>

int Parse(int a[],int n);

void dfs(int a[], int pos,int n);

int main()

{

int a[17]={1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9};

// 数组a中a[1]、a[3]、…、a[15]用0、1、2保存可能的运算符

// 其中,0代表空格、1代表加号+、2代表减号-

int n;

scanf("%d",&n);

dfs(a,1,n);

return 0;

}

void dfs(int a[], int pos,int n)

{

if(pos == 2*n-1)     // 已赋了N-1个位置的算符

{

if (Parse(a,n)==0)

{

char b[3]={' ','+','-'};

for(int i=0;i<2*n-2;i++)

if (i%2==0) printf("%d",a[i]);

else  printf("%c",b[a[i]]);

printf("%d\n",a[2*n-2]);

}

return;

}

for(int i = 0;i <= 2;i++)

{

a[pos] = i;

dfs(a,pos+2,n);

}

}

int Parse(int a[],int n)

{

int value=0,i,preVal,preCalc;

preVal=a[0]; preCalc=1;

for(i=1;i<2*n-1;i=i+2)

{

switch (a[i])

{

case 0: preVal=preVal*10+a[i+1];

break;

case 1:

case 2: if (preCalc==1)

value=value+preVal;

else

value=value-preVal;

preCalc=a[i];

preVal=a[i+1];

}

}

if (preCalc==1)

value=value+preVal;

else

value=value-preVal;

return value;

}

16-2  填数字游戏

问题描述

设有等式ABCD*E=DCBA,A、B、C、D、E代表的数字各不相同,编程求A、B、C、D、E。

输入样例

无输入

输出样例

2178*4=8712

(1)编程思路1

直接对a(1<=a<=4)、b(0<=b<=9)、c(0<=c<=9)、d(1<=d<=9)、e(2<=e<=9)五个数字的各种取值情况进行穷举,用s表示四位数ABCD(即s=1000*a+100*b+10*c+d)、k表示四位数DCBA(即k=a+10*b+100*c+1000*d),若满足s*e==k且a、b、c、d、e互不相等(即a!=b && a!=c && a!=d  && a!=e && b!=c && b!=d && b!=e && c!=d && c!=e && d!=e),则找到问题的解。

(2)源程序1。

#include <stdio.h>

int main()

{

int a,b,c,d,e,s,k;

for(a=1;a<=4;a++)

for (b=0;b<=9;b++)

for(c=0;c<=9;c++)

for(d=1;d<=9;d++)

for (e=2;e<=9;e++)

{

s=1000*a+100*b+10*c+d;

k=a+10*b+100*c+1000*d;

if(s*e==k && a!=b && a!=c && a!=d  && a!=e

&& b!=c && b!=d && b!=e && c!=d && c!=e && d!=e)

{

printf("%d*%d=%d\n",s,e,k);

}

}

return 0;

}

(3)编程思路2

由于等式中ABCD是一个四位数,最高位A在1~4之间,且各位数字互不相等,因此,可以在1023~4987之间穷举找到满足条件的四位数。

为判断四位数ABCD的各位数字和数字E均互不相等,编写一个函数int isRepeat(int n,int e),若整数n的各位数字和整数e均互不相等,函数返回值1,否则返回0。判断方法是:定义一个一维数组b[10],用于保存0~9各个数字出现的次数,将整数n的各位数字分解出来,每分解出一个数字i,对应的数组元素b[i]加1,数字e对应的数组元素b[e]也加1,然后统计数组b中值为1的元素个数cnt,若cnt等于整数n的位数加1,则每个数字出现一次,即各个数字互不相等,函数返回1。

由于等式中四位数DCBA是ABCD的逆序数,因此编写一个函数int reversenum(int n)用于求整数n的逆序数。

(4)源程序2。

#include <stdio.h>

int isRepeat(int n,int e);

int reversenum(int n);

int main()

{

int i,j,k;

for(i=1023;i<=4987;i++)

{

for (j=2;j<=9;j++)

{

if (isRepeat(i,j)==1) continue;

k=reversenum(i);

if (i*j==k)

printf("%d*%d=%d\n",i,j,k);

}

}

return 0;

}

int isRepeat(int n,int e)

{

int m=0,b[10]={0},i,cnt;

while (n!=0)

{

i=n%10;  b[i]++;

n=n/10;  m++;

}

b[e]++;

cnt=0;

for(i=0;i<=9;i++)

if(b[i]!=0)    cnt++;

if(cnt!=m+1)  return 1;

else        return 0;

}

int reversenum(int n)

{

int m=0;

while(n!=0)

{

m=m*10+n%10;

n=n/10;

}

return m;

}

按编程思路2编写的程序,比编程思路1灵活多了。如题目改为设有等式ABCDE*F=EDCBA,A、B、C、D、E、F代表的数字各不相同,编程求A、B、C、D、E、F。只需将程序中的穷举范围改变即可。

将程序中的语句“for(i=1023;i<=4987;i++) ”改写为“for(i=10234;i<=49876;i++)”,重新编译并执行以上程序,得到如下所示的结果。

21978*4=87912

16-3  马虎的算式

问题描述

小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。有一次,老师出的题目是:36 x 495 = ?  他却给抄成了:396 x 45 = ?   但结果却很戏剧性,他的答案竟然是对的!因为 36 * 495 = 396 * 45 = 17820

类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54。

假设a、b、c、d、e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0),能满足形如:ab * cde = adb * ce 这样的算式一共有多少种呢?

输入格式

一个正整数n(5≤n≤9),表示a、b、c、d、e 代表1~n不同的5个数字。

输出格式

一个整数,表示满足形如 ab * cde = adb * ce 这样的算式的种数。

输入样例

9

输出样例

142

(1)编程思路1。

本题关键是穷举a、b、c、d、e在1~n之间取5个不同数所构成的全排列。

定义一个数组int num[6],其中num[1]~num[5]五个元素分别表示a、b、c、d、e的取值。定义一个数组int visit[10]={0}标记数字是否出现,如visit[3]=1表示数字3已被使用,visit[3]=0表示数字3还未被使用。编写如下的递归函数可以生成1~n中任取5个数字构成的全排列。

void dfs(int num[], int pos,int n, int visit[])

{

if(pos ==6)     // 已有5个数

{

for (int i=1;i<6;i++)

printf(“%d ”,num[i]);

printf(“\n”);

return;

}

For (int i = 0 ;i <= n;i++)

{

if(!visit[i])

{

num[pos] = i;

visit[i] = 1;

dfs(num,pos+1,n,visit);

visit[i] = 0;

}

}

}

生成1~n不同的5个数字存储到数组中后,检测对应算式ab*cde == adb*ce是否成立,如果成立,是一组解,输出并计数。

(2)源程序1。

#include <stdio.h>

int cnt=0;

void dfs(int num[], int pos,int n,int visit[])

{

if(pos == 6)     // 已有5个数

{

int ab = num[1]*10 + num[2];

int cde = num[3]*100 + num[4]*10 + num[5];

int adb = num[1]*100 + num[4]*10 + num[2];

int ce = num[3]*10 + num[5];

if(ab*cde == adb*ce)

{

cnt++;

}

return;

}

for(int i = 1;i <= n;i++)

{

if(!visit[i])

{

num[pos] = i;  visit[i] = 1;

dfs(num,pos+1,n,visit);

visit[i] =0;

}

}

}

int main()

{

int num[6],n;

int visit[10];

scanf("%d",&n);

for (int i=1;i<=9;i++)

visit[i]=0;

dfs(num,1,n,visit);

printf("%d\n",cnt);

return 0;

}

当然,本题不用递归,采用循环进行穷举也能很方便地解决。编写的循环穷举程序如下源程序2所示。

(3)源程序2。

#include <stdio.h>

int main()

{

int n;

scanf("%d",&n);

int a,b,c,d,e,cnt=0;

for(a = 1; a <= n;a++)

for(b = 1; b <= n;b++)

for(c = 1; c <= n;c++)

for(d = 1; d <= n;d++)

for(e = 1; e <=n;e++)

{

int ab = 10*a + b;

int cde = 100*c + 10*d + e;

int adb = 100*a + 10*d + b;

int ce = 10*c + e;

if(ab*cde != adb*ce)

continue;

int num[10],i;

for (i=1;i<=9;i++)

num[i]=0;

num[a]++;  num[b]++;  num[c]++;

num[d]++;  num[e]++;

for(i = 1;i <= 9;i++)

if(num[i] > 1)  break;

if(i == 10)

{

cnt++;

}

}

printf("%d\n",cnt);

return 0;

}

C语言程序设计100例之(16):巧解算式的更多相关文章

  1. 黑马程序员——经典C语言程序设计100例

    1.数字排列 2.奖金分配问题 3.已知条件求解整数 4.输入日期判断第几天 5.输入整数进行排序 6.用*号显示字母C的图案 7.显示特殊图案 8.打印九九口诀 9.输出国际象棋棋盘 10.打印楼梯 ...

  2. C语言程序设计100例之(28):直线蛇形阵

    例28        直线蛇形阵 问题描述 编写程序,将自然数1.2.….N2按蛇形方式逐个顺序存入N阶方阵.例如,当N=3和N=4时的直线蛇形阵如下图1所示. 图1  直线蛇形阵 输入格式 一个正整 ...

  3. C语言程序设计100例之(27):回旋方阵

    例27        回旋方阵 问题描述 编写程序,生成从内到外是连续的自然数排列的回旋方阵.例如,当n=3和n=4时的回旋方阵如下图1所示. 图1  由内到外回旋方阵 输入格式 一个正整数n(1≤n ...

  4. C语言程序设计100例之(31):全排列问题

    例31   全排列问题 题目描述 输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入格式 n(1≤n≤9) 输出格式 由1-n组成的所有不重复的数字 ...

  5. C语言程序设计100例之(9):生理周期

    例9    生理周期 问题描述 人生来就有三个生理周期,分别为体力.感情和智力周期,它们的周期长度为 23 天.28 天和33 天.每一个周期中有一天是高峰.在高峰这天,人会在相应的方面表现出色.例如 ...

  6. C语言程序设计100例之(14):丑数

    例14   丑数 问题描述 丑数是其质因子只可能是2,3或5的数.前10个丑数分别为1, 2, 3, 4, 5, 6, 8, 9, 10, 12.输入一个正整数n,求第n个丑数. 输入格式 每行为一个 ...

  7. C语言程序设计100例之(17):百灯判亮

    例17   百灯判亮 问题描述 有序号为1.2.3.….99.100的100盏灯从左至右排成一横行,且每盏灯各由一个拉线开关控制着,最初它们全呈关闭状态.有100个小朋友,第1位走过来把凡是序号为1的 ...

  8. C语言程序设计100例之(25):确定进制

    例25    确定进制 问题描述 6*9 = 42 对于十进制来说是错误的,但是对于13进制来说是正确的.即 6(13)* 9(13)= 42(13),因为,在十三进制中,42 = 4 * 13 + ...

  9. C语言程序设计100例之(22):插入排序

    例22  插入排序 问题描述 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素或记录的任意序列,重新排列成一个以关键字递增(或递减)排列的有序序列. 排序的方法有很多,简单插入排序就是一 ...

随机推荐

  1. Newman

    目录 简介 安装 使用 简介 Newman是为postman而生,专门用来运行postman编写好的脚本 使用Newman,你可以很方便的用命令行来执行postman collections Newm ...

  2. 大数据学习笔记——Hadoop编程实战之Mapreduce

    Hadoop编程实战——Mapreduce基本功能实现 此篇博客承接上一篇总结的HDFS编程实战,将会详细地对mapreduce的各种数据分析功能进行一个整理,由于实际工作中并不会过多地涉及原理,因此 ...

  3. 5分钟搞清楚Synchronized和Lock的概念与区别

    前言 并发编程中,锁是经常需要用到的,今天我们一起来看下Java中的锁机制:synchronized和lock. Synchronized 和 Lock的概念 Synchronized 是Java 并 ...

  4. 基于Vue的前后端分离项目实践

    一.为什么需要前后端分离 1.1什么是前后端分离  前后端分离这个词刚在毕业(15年)那会就听说过,但是直到17年前都没有接触过前后端分离的项目.怎么理解前后端分离?直观的感觉就是前后端分开去做,即功 ...

  5. 安装包RPM包或源码包

    RPM工具 # mount /dev/cdrom /mnt     挂载光盘 # rpm     软件包管理器 -i     安装(需要安装包完整名称) -v    可视化 -h    显示安装进度 ...

  6. linux 动态链接库查找方法;查找动态链接库位置; LIBRARY_PATH 和 LD_LIBRARY_PATH 的区别;LD_LIBRARY_PATH and LD_RUN_PATH的区别;

    今天配置之前项目的时候,发现有些动态链接库变了,想看看现在应用在使用哪些动态链接库的时候,进一步查了点资料: 下面针对linux动态链接库查找方法和动态链接库位置配置的过程进行记录: LIBRARY_ ...

  7. 微软与阿里云合作推出“开放应用模型(OAM)”

    英文原文:Announcing the Open Application Model (OAM) 原文标题:微软与阿里云合作推出“开放应用模型(OAM)” 用于 Kubernetes 及更多平台的应用 ...

  8. c代码中while循环的一个死机问题引发的思考

    前记   c语言已经是一门经常吃饭的本领,本来是要有种看一眼,就知道哪儿出问题了才行,没想到,遇到实际问题的时候,才知道自己的修为不到家.还没有达到那种炉火纯青的境界.看来,不是这个世界没有机会,是自 ...

  9. python3数据分析,安装学习

    python3数据分析,安装学习 转载注明来源: 本文链接 来自osnosn的博客,写于 2019-09-26. 为了简单.安装 anaconda3 就好啦. 因为安装原版python3,用pip安装 ...

  10. Java 9 ← 2017,2019 → Java 13,来看看Java两年来的变化

    距离 2019 年结束,只剩下 33 天了.你做好准备迎接 2020 年了吗? 一到年底,人就特别容易陷入回忆和比较之中,比如说这几天的对比挑战就火了! 这个话题登上了微博的热搜榜,也刷爆了朋友圈, ...