导航菜单
首页 >  历年蓝桥杯真题c语言  > 蓝桥杯:带分数(全排列)

蓝桥杯:带分数(全排列)

  历届试题 带分数  时间限制:1.0s   内存限制:256.0MB问题描述

100 可以表示为带分数的形式:100 = 3 + 69258 / 714。

还可以表示为:100 = 82 + 3546 / 197。

注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。

类似这样的带分数,100 有 11 种表示法。

输入格式

从标准输入读入一个正整数N (N 1)//如果存在某个数据不只有1个return 1;//返回冲突return 0;//返回正确 }//检查是否每个数据都已经存在 int checkAll(void){ for(int i = 1; i < 10; i++) if(flag[i] != 1)return 1;//不全部包括return 0;//全部包括 }int main(void){ int i = 0; int num = 0;//输入数据 int count = 0; //统计个数 scanf("%d", &num); int left = 0, up = 0, down = 0;//带分数 for(left = 1; left < num; ++left) //先从带分数的左边开始增加 { for (i = 0; i < 10; ++i)//将标记数组全部初始化为0 flag[i] = 0; if(check(left)) //检查左边数据合理性continue;//不合理for (i = 0; i < 10; ++i)//缓存flag数据,用于下面for循环重置 backup[i] = flag[i]; for(down = 1; down < 100000; ++down) //再增加带分数底数 { for (i = 0; i < 10; ++i) //重置flagflag[i] = backup[i];up = (num - left) * down; //计算出up值if(check(down) || check(up)) //检查数据合理性 continue;//不合理if(!checkAll()) {//printf("%d = %d + %d / %d\n", num, left, up, down);++count; //计数+1}} } printf("%d\n", count);//打印总共个数 return 0;}KeepThinking_的全排列算法:

根据题目的要求,输入一个数字,需要将这个数字化成带分数,且包含(0-9)所有数字(不重复)。

这里我们假设输入的数字为number,划分好的带分数为a+b/c,它将满足条件number == a+b/c且b%c==0,同时abc包含所有(0-9,且不重复)。

请注意最后最句话,abc包含所有(0-9,且不重复),我们再看题目给我们的那个满足条件的数字:

3+69258/714   这里a = 3, b = 69258, c = 714 ;则abc = 369258714(这个数字数list的一种排列方式)。

所以我们想到一种方法:先求出list的一种排列,然后对这个排列进行a,b,c划分处理,如果满足条件

(条件:number == a+b/c且b%c==0,同时abc包含所有(0-9,且不重复))则将记录种数+1 。

在看KeepThinking_的全排列算法之前,我们需要知道如何对数据进行全排列,这里我们介绍两种方法:字典序法和递归分治法。

先看字典序法

我们先看一个例子。

示例: 1 2 3的全排列如下:

1 2 3 , 1 3 2 , 2 1 3 , 2 3 1 , 3 1 2 , 3 2 1

我们这里是通过字典序法找出来的。

那么什么是字典序法呢?

从上面的全排列也可以看出来了,从左往右依次增大,对这就是字典序法。可是如何用算法来实现字典序法全排列呢?

我们再来看一段文字描述:(用字典序法找124653的下一个排列)

你主要看红色字体部分就行了,这就是步骤。

如果当前排列是124653,找它的下一个排列的方法是,从这个序列中从右至左找第一个左邻小于右邻的数,

如果找不到,则所有排列求解完成,如果找得到则说明排列未完成。

本例中将找到46,计4所在的位置为i,找到后不能直接将46位置互换,而又要从右到左到第一个比4大的数,

本例找到的数是5,其位置计为j,将i与j所在元素交换125643,

然后将i+1至最后一个元素从小到大排序得到125346,这就是124653的下一个排列。

下图是用字典序法找1 2 3的全排列(全过程)。

将算法转化为C语言代码,如下:

附录:(全排列:字典序法)

/*Name: 全排列(字典序法) Copyright: 供交流 Author: JopusDate: 09/02/14 17:39Description: dev-cpp 5.5.3*//*字典序法(算法)第一步:从右往左,找出第一个左边小于右边的数,设为list[a] (图中红色字体)。第二步:从右往左,找出第一个大于list[a]的数,设为list[b](图中浅蓝色字体)。第三步:交换list[a],list[b]。第四步:将list[a]后面的数据,由小往大排列(图中淡绿色字体)。 */ #include //交换list[a],list[b] void Swap(int list[], int a, int b){int temp = 0;temp = list[a];list[a] = list[b];list[b] = temp;return;}//将list区间[a,n]之间的数据由小到大排序 void Sort(int list[], int a, int n){int temp = 0;for (int i = 1; i < n-a; ++i)for (int j = a+1; j < n-1; ++j)if (list[j] > list[j+1]){temp = list[j];list[j] = list[j+1];list[j+1] = temp;}return;}//全排列 void Prim(int list[], int n){int num = 1, a = 0, b = 0;for (int i = n; i > 0; --i) //计算有多少种情况,就循环多少次 num *= i;while (num--){for (int i = 0; i < n; ++i) //打印情况 printf("%d ",list[i]);printf("\n");for (int i = n-1; i > 0; --i) //从右往左,找出第一个左边小于右边的数,设为list[a] if (list[i-1] < list[i]){a = i-1;break; }for (int j = n-1; j > a; --j) //从右往左,找出第一个大于list[a]的数,设为list[b] if (list[j] > list[a]){b = j;break;}Swap(list, a, b); //交换list[a],list[b] Sort(list, a, n); //将list[a]后面的数据,由小往大排列 }return;}//主函数 int main(){int list[] = {1,2,3,4};Prim(list,3);return 0;}当然,如果你觉得这种方法麻烦,我们再看一种全排列算法(递归版)。

递归分治法

下图是对(1,2,3,4)全排列的树状图:

将图示转化为C语言代码(如下):

#include /*递归(分治法思想):设(ri)perm(X)表示每一个全排列前加上前缀ri得到的排列.当n=1时,perm(R)=(r) 其中r是唯一的元素,这个就是出口条件.当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),...(rn)perm(Rn)构成.*///此处为引用,交换函数.函数调用多,故定义为内联函数.inline void Swap(int &a,int &b){int temp=a;a=b;b=temp;}void Perm(int list[],int k,int m) //k表示前缀的位置,m是要排列的数目.{if(k==m-1) //前缀是最后一个位置,此时打印排列数.{for(int i=0;iB//将前缀换回来,继续做上一个的前缀排列.//>>CSwap(list[k],list[i]);}}}//主函数 int main(){int temp = 0;int list[] = {1,2,3,4,5,6,7,8,9}; //定义全排列数组 scanf("%d",&number);temp = number;while (temp != 0) //统计number总共多少位 { ++x; temp /= 10; } Prim(list,0,9);printf("%d", count);//打印总共多少种 return 0;}

提交序号 姓名 试题名称 提交时间  代码长度                      CPU使用  内存使用  评测详情 63929 Jopus 带分数 02-09 00:36 2.544KB C++ 正确 100 46ms 808.0KB 评测详情

转载请保留原文地址:http://blog.csdn.net/jopus/article/details/18998403

相关推荐: