例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. drf源码分析系列---版本控制

    版本的使用 第一步:写路由url(r'^api/(P<version>\w+)/user/$',views.UserView.as_view()), 第二步:写模块导入from rest_ ...

  2. php5.4.16执行shell脚本

    因为要用到Python脚本,所以打算直接在PHP中直接调用系统命令system(). 要注意两点: 一.PHP5.3以上不存在安全模式,即要直接执行脚本不需要作任何其他配置. 二.system函数格式 ...

  3. JS基础知识——原型与原型链

    1.如何准确判断一个变量的数组类型 2.写一个原型链继承的例子 3.描述new一个对象的过程 4.zepto(或其他框架中如何使用原型链) 知识点: (1)构造函数 function Foo(name ...

  4. Linux中的 date 使用

    01. 日期格式字符串列表 %H 小时(以00-23来表示). %I 小时(以01-12来表示). %K 小时(以0-23来表示). %l 小时(以0-12来表示). %M 分钟(以00-59来表示) ...

  5. springboot启动原理解析

    springboot核心原理: 1.基于springmvc无配置文件完全注解化 + 内置web容器实现springboot框架.main函数方式的启动 2.通过maven快速整合第三方框架 sprin ...

  6. Nginx配置实例-反向代理实例:根据访问的路径跳转到不同端口的服务中

    场景 Ubuntu Server 16.04 LTS上怎样安装下载安装Nginx并启动: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/detai ...

  7. C#字符串与时间格式化

    需要将其它类型的变量,转换为字符串类型的一些常见方法与属性. 字符型转换为字符串 // C 货币 2.5.ToString("C"); // ¥2.50 // D 10进制数 .T ...

  8. com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details

    1.错误显示 com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details log提示:Generate Si ...

  9. linux I/O重定向及管道

    一,I/O重定向 重定向正常输出 > :覆盖以存在文件的内容,很危险的操作 >>:如果文件已经存在,则是在原内容的最后追加. 可以禁止>的覆盖行为,使用set -C.只在当前会 ...

  10. Centos7 死活上不了网

    NAT模式,手动修改ifcfg 如下: # vi /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE=EthernetPROXY_METHOD=noneBR ...