蓝桥杯 带分数
来源:互联网 发布:网络信息安全问题介绍 编辑:程序博客网 时间:2024/05/04 23:24
这可以看成是一道全排列的问题
思路:
1、全排列出所有可能的数列(全排列问题留到稍后说)
2、对数列进行分析判断,其中:
num = num1 + num2 / num3; 相当于是将数列进行截断,直接暴力截断是不可能的,时间实在太长了,参考别人的想法进行剪枝:
(1)求num的长度为len,可知num1的长度不大于len
(2)由于num是整数,所以num2 > num3(即 num2 长度大于等于余下长度的1/2) 且num2 % num3 == 0
(这个剪枝的想法参考于http://blog.csdn.net/skillart/article/details/8902478)
(3)涉及将数字转化成字符串以及将区间内数字转化为一个整数
我第一次写的代码:
#include<stdio.h> #include<stdlib.h>#include<string.h>long long aim;int n = 0; int len = 0;// 数组区间转化为数字的函数 int getsum( int list[], int f, int e){ int sum = 0; int i; for( i = f; i <= e; i++){ sum = list[i] + sum * 10; } return sum; } // 判断函数 void judge( int* list){ int i, j, num1, num2, num3; for( i = 0; i < len; i++){ num1 = getsum( list, 0, i); for( j = i + ( 8 - i) / 2; j < 8; j++){ num2 = getsum( list, i+1, j); num3 = getsum( list, j+1, 8); if( num2 % num3){ continue; } else if( num1 + num2 / num3 == aim){ n++; } } } } //交换函数 void swap(int* list, int a, int b) { int m; m = list[a]; list[a] = list[b]; list[b] = m; //交换两个指针所指的数 } //全排列函数 void perm(int list[], int k, int m) { int i; if( k > m) //两个指针 { judge(list); } else //头指针大等于尾指针时 { for(i = k; i <= m; i++) { swap(list, k, i); perm(list, k + 1, m); swap(list, k, i); } } } int main() { int list[] = {1,2,3,4,5,6,7,8,9}; char str[10]; scanf("%I64d", &aim); sprintf( str, "%d", aim); //啊啊,这里的sprintf是我第一次用,非常好用啊 len = strlen(str); perm(list, 0, 8); printf("%d", n); return 0; }
之后进行了一些算法的调整: 好像比较快了
// 判断函数 void judge( int* list){ int i, j, a, b, c; int blast; for( i = 0; i < len; i++){ a = getsum( list, 0, i); blast = (( aim - a) * list[8]) % 10; for( j =i+(8-i)/2; j < 8; j++)//从list数组中间位置开始找b末尾位置 { if(list[j]==blast) //找到b尾部 { b = getsum(list, i+1, j); //将list数组中的[i+1-j]转化为数字,赋值给b c = getsum(list, j+1, 8); //将list数组中的[j+1-8]转化为数字,赋值给c if (b % c == 0 && a + b / c == aim) //判断合理性 { ++n; } break; } } } }
我借此机会可以研究一下全排列。其实在2015预赛已经考过了,当初做出来那只是运气好,还是需要认真的研究一下的。
主要参考了这个作者的一些讲解,他讲的很清楚,我算是在做一个读后感
http://www.cnblogs.com/nokiaguy/archive/2008/05/11/1191914.html
对全排列的求法有两种:字典序法和递归分治法
【1】字典序法:
字典序 1 2 3的全排列如下: 1 2 3 , 1 3 2 , 2 1 3 , 2 3 1 , 3 1 2 , 3 2 1
设 123456 是字典序最小的序列,654321是字典序最大的序列,我们在寻找任一序列的字典序下一序列:如 124653
(1)从右至左寻找某数满足:左侧数<右侧数,a 标记为左侧数 ( i = 4 )
(2)从右至左寻找 i 前最早出现的某数大于 i ,b 标记为此数(i = 5)
(3)将 j 和 i 交换 得 125643
(4)将 i+1 及以后的数从小到大排列 得 125346
如字典序下一个序列是 125346 【事实上就是在寻找比 124653 大的最小的数 ——总的来说最大的数上浮】
#include<stdio.h>void swap(int *a, int *b) // 交换函数 { int m; m = *a; *a = *b; *b = m; }void sort( int list[], int a, int b){ // 冒泡排序int i, j;for( i = b; i > a; i--){for( j = a; j < i; j++){if( list[j] > list[j+1]){swap( &list[j], &list[j+1]);}}}} int main(){ int list[] = {1, 2, 3, 4}; int i, j, a, b, n = 24; while(n--){ for( i = 0; i < 4; i++)printf("%d ", list[i]); //打印排列printf("\n"); for( i = 3; i > 0; i--){ // 判断是否为(1)情况,标记a if( list[i-1] < list[i]){ a = i - 1; break; } } for( j = 3; j >= a; j--){ // 判断是否为(2)情况,标记bif( list[j] > list[a]){b = j;break;}} swap(&list[a], &list[b]); // 交换 sort( list, a + 1, 3); //执行(3) } return 0; }
【2】递归分治法
这个想了比较久,多少还是懂了
在分析之前的那个方法之前,先分析一下刘汝佳书上的一种方法,比较简单易懂。基本上是这样的:
(1)将待排列数列分成两列序列A,和集合S;(S不必存储,因为可以被A完全确定)
(2)从小到大的顺序以此考虑S中的每一个元素V { 若V已经被使用, 则跳过; 若V未被使用, 则使用,加入数组A,并标记}
(3)当S中没有元素,则输出A
这是一种递归调用实现字典序的办法,其核心是一种 确定数列前缀的思想:A为前缀序列,依次将S中的数加入A,然后进行全排列。
之后更加简洁的方法就出现了,重点是这里
for(i = k; i <= m; i++) { swap(&list[k], &list[i]); //将K和i进行交换,k以前的数生成新的前缀 perm(list, k + 1, m); //进行全排列 swap(&list[k], &list[i]); //将k和i换回来,返回到上一层 }
交换的过程相当于将K以前的数作为前缀,之后进行全排列
这个网址说的很清楚http://www.sjsjw.com/kf_code/article/21_12537_14709.asp。以下引用:
(注:(A)表示执行语句(A),见代码后标记。(B0)表示第0层递归,相当于上图中的根结点结点{1,2,3,4})
第一步:i = k = 0; 交换list[0]←→list[0](A),即将list[0]加入到前缀得到如结点(b)所示,然后对{2,3,4}全排列。(B0)(B0表示第0层递归,下同)
第二步:i = k = 1;交换list[1]←→list[1](A),即将list[1]加入到前缀得到如结点(c)所示,然后对{3,4}全排列。(B1)
第三步:i = k = 2;(进入Prim)交换list[2]←→list[2](A),即将list[2]加入到前缀得到如结点(f)所示,然后对{4}全排列。(B2)
第四步:i = 2,k = 3,(进入Prim)打印第三步的情况,即打印结点(f)。(B3)
第五步:i = k = 2,交换list[2]←→list[2](C),即还原到交换前状态。(B2)
第六步:i = 3, k = 2,(for循环)交换list[3]←→list[2](A),即将list[3]加入前缀得到如结点(g)所示,然后对{3}全排列。(B2)
第七步:i = 3,k = 3,(进入Prim)打印第六步的情况,即打印结点(g)。(B3)
第八步:i = 3, k = 2,交换list[3]←→list[2](C),即还原到交换前状态。(B2)
第九步:i = 4, k = 2,跳出for循环,第一个分支打印完毕,准备打印第二个分支。(B2->B1)
第十步:i = k = 1,交换list[1]←→list[1](C),即还原到交换前状态。(B1)
第十一步:i = 2, k = 1,交换list[2]←→list[1](A),即将list[2]加入前缀得到如结点(d)所示,然后对{2,4}全排列。(B1)
第十二步:i = 2, k = 2,(进入Prim)交换list[2]←→list[2](A),即将list[2]加入到前缀得到如结点(h)所示,然后对{4}全排列。(B2)
第十三步:i = 2,k = 3,(进入Prim)打印第十二步的情况,即打印结点(h)。(B3)
......................
不一一列举了,(i)就相当于交换到第几个了,(k)相当于在第几层。
好了,全排列就讲到这里了
- 【蓝桥杯】带分数
- 带分数 - 蓝桥杯
- 蓝桥杯,带分数
- 蓝桥杯 带分数
- 蓝桥杯-带分数
- 蓝桥杯:带分数
- 蓝桥杯 带分数
- 蓝桥杯---带分数
- 蓝桥杯 带分数
- 蓝桥杯 带分数
- 蓝桥杯带分数
- 蓝桥杯 带分数
- 蓝桥杯-带分数
- 蓝桥杯 带分数
- 带分数 蓝桥杯
- 蓝桥杯 带分数
- 蓝桥杯 带分数
- 蓝桥杯--带分数
- 黑盒测试及其实例
- 字符匹配
- Java项目中,如何实现按回车键实现登录!
- JAVA基础 —— 继承、抽象、接口
- Android不透明度16进制值
- 蓝桥杯 带分数
- Citrix XenServer 6.5以及XenCenter 6.5官方版本下载地址
- Chapter 3
- Deep learning Reading List
- 关于爱情
- ProcDOT visual malware analysis
- 将命令行的输出重定向到某个文件
- 正则表达式的特殊用法
- 2015前端框架何去何从