实用算法实践-第 30 篇 组合数学
2012-02-08 23:38
309 查看
30.1 Ctalan数
PKU JudgeOnline, 1095, Trees Made to Order.30.2 Fibonacci数
Fibonacci数列的定义如下:f(n) = f(n - 1) + f(n - 2) (n >= 3)
f(1) = 1, f(2) = 2
f(0)可定义为1。
用归纳法可以证明性质:
f(n + m) = f(m - 1)f(n + 1) + f(m - 2)f(n) (m>= 2)
利用这条性质,我们可以将比较大的n的Fibonacci数转化成比较小的Fibonacci数,从而使计算起来更为方便。
这里有一个问题:
Fibonacii数列 Fn (mod k) 的循环节长度是多少?有没有关于k的通项公式或者计算方法?
30.2.1 实例
PKU JudgeOnline, 3070, Fibonacci.30.2.2 问题描述
给定n,要求第n个Fibonacci数mod 10000的结果。30.2.3 输入
09
999999999
1000000000
-1
30.2.4 输出
034
626
6875
30.2.5 分析
可以通过Fibonacci的性质和模运算的性质对之进行递归求解。通过鸽巢原理可以知道输出必定是一个循环。如果知道循环节,该问题就更简单了。循环节也很好找。30.2.6 程序
#include <iostream> #include <math.h> #include <string.h> using namespace std; #define repeatTime 50 #define __int32Max 2147483648 //0x7FFFFFFF = 2147483647 = 536870911.75 * 4 #define maxSize 5000000 int F[maxSize]; int ModularFibonacci(int n, int p) { //0, 1, 1, 2, 3, 5, 8, 13, 21, 34, … //0 1 2 3 4 5 6 7 8 9 int temp; if(n < 0) return 0; if(n < maxSize && F != 0){ return F ; } if(n % 2 == 0){ temp = ((ModularFibonacci(n / 2 - 1, p) * ModularFibonacci(n / 2 + 1, p)) + (ModularFibonacci(n / 2 - 2, p) * ModularFibonacci(n / 2, p))) % p; }else{ temp = ((ModularFibonacci(n / 2, p) * ModularFibonacci(n / 2 + 1, p)) + (ModularFibonacci(n / 2 - 1, p) * ModularFibonacci(n / 2, p))) % p; } if(n < maxSize){ F = temp; } return temp; } int main() { int n; memset(F, 0, sizeof(F)); F[0] = 1; F[1] = 1; F[2] = 2; F[3] = 3; F[4] = 5; while(cin >> n && n != -1){ cout<< ModularFibonacci(n - 1, 10000) << endl; } return 1; }
30.3 Pólya计数定理
Pólya计数定理是组合数学种一个非常重要的定理。它可以用来解决类似于计算不同着色方案数的问题。
《算法艺术与信息学竞赛》从着色方案的角度介绍了Pólya计数定理。相对于组合数学教材中的Pólya计数定理的形式化描述,该介绍较为容易理解。不过似乎该文介绍的只是Pólya计数定理的一种特殊化表达。
30.3.1 实例
PKU JudgeOnline, 2409, Let it Bead.30.3.2 问题描述
一个手镯由c种颜色的s个珠子串起来。问对已知的c和s,手镯可以有多少种。30.3.3 输入
1121
22
51
25
26
62
00
30.3.4 输出
12
3
5
8
13
21
30.3.5 分析
这个问题是基本的着色问题。解决这类问题,可按照如下的步骤进行:1. 分析置换群的种类和个数。
2. 分析每个置换的循环分解式和循环节。
3. 根据Pólya计数定理计算方案数。
对于这个问题,可以知道置换既可以是为旋转(rotation),也可以是翻转(reflection)。旋转和翻转的类型分别为s种。旋转每增加一个360/s度,对应于升序置换的一次循环右移。翻转轴旋转一个360/s度,对应于降序置换的一次循环右移。所以置换很容易计算出来。
有了置换的具体表示,其循环分解式和循环节就很容易计算出来了。
对每种置换进行计算,根据公式求和,就算出了着色的不同方案。
30.3.6 程序
#include<string.h> #include<stdio.h> #include<iostream> using namespace std; int permutation[100]; int c; int s; void visit1(int i) {//输出循环分解式 int j; int temp; j = i; while(permutation[j]!= i){ cout << j <<" "; temp = permutation[j]; permutation[j] = 0; j = temp; } cout << j <<" "; permutation[j] = 0; cout << endl;; } void visit(int i) {//不输出循环分解式,仅用来计算循环节 int j; int temp; j = i; while(permutation[j]!= i){ temp = permutation[j]; permutation[j] = 0; j = temp; } permutation[j] = 0; } int cycleNum() {//计算置换的循环节 int num; int i; num = 0; for(i = 1;i <= s; i++){ if(permutation[i]!= 0) { visit(i); num++; } } return num; } int exponent(int a, intb) {//计算a^b b必须大于 int i; int temp; temp = 1; for(i = 0;i < b; i++){ temp = temp * a; } returntemp; } int main() { int i, j; intgroupSize; int sum; int temp; while(cin>> c >> s){ if(c ==0 && s == 0) { break; } //cout<< cycleNum(); sum = 0; for(i =0; i < s; i++){ for(j= 1; j <= s - i; j++){ permutation[j] = j + i; } for(;j <= s; j++){ permutation[j] = j + i - s; } temp = cycleNum(); sum += exponent(c, temp); } for(i =1; i <= s; i++){ for(j= 1; j <= i; j++){ permutation[j] = i - j + 1; } for(;j <= s; j++){ permutation[j] = i + s - j +1; } temp = cycleNum(); sum += exponent(c, temp); } groupSize = s << 1; sum = sum / groupSize; cout << sum << endl; } }
本文章欢迎转载,请保留原始博客链接http://blog.csdn.net/fsdev/article
相关文章推荐
- 实用算法实现-第 30 篇 组合数学
- Java编程算法基础----组合数学实践
- 算法_3 : 组合数学:排列组合
- 实用算法实践-第 32 篇 其它
- 实用算法实践-第 2 篇 有序数组的二分查找
- 实用算法实践-第 4 篇 散列表
- 按字典序生成{1,2,...,n}的r子集的算法-组合数学
- 实用算法实践-第 3 篇 堆排序
- 实用算法实践-第 4 篇 散列表
- 04-06组合数学实践_交通问题
- 实用算法实践-第 28 篇 素数判别
- 实用算法实践-第 3 篇 堆排序
- 算法--组合数学:杨辉三角数学分析以及Java实现
- 实用算法实践-第 29 篇 计算几何学
- 51nod 算法马拉松30 A.函数【容斥】【组合数学】
- 数学中的组合算法(sql)
- C/C++面试之算法系列--如何利用数学思想解1/2/5组合问题
- 中国数学建模-编程交流-组合算法概论
- 项目简单实用方式_组合替代继承_算法切换
- 【2015暑假】鸽巢原理总结 【算法思路+组合数学】