├── README.md ├── leecode ├── 104. Maximum Depth of Binary Tree.md ├── 168. Excel Sheet Column Title.md ├── 231. Power of Two.md ├── 292.Nim Game.md ├── 342.Power of Four.md ├── 344.Reverse String.md └── 349. Intersection of Two Arrays.md ├── 源码 ├── 1-1.cpp ├── 1-2.cpp ├── 2-1.cpp ├── 2-2.cpp ├── 2-3.cpp ├── 2-4.cpp ├── 2-5.cpp ├── 2-6.cpp ├── 2-7.cpp ├── 2-8.cpp ├── 2-9.cpp ├── 3-1.cpp ├── 3-2.cpp ├── 3-3.cpp ├── 3-4.cpp ├── 3-4.java ├── 3-6.cpp ├── 4-1.cpp ├── 4-2.cpp ├── 5-1.cpp ├── 5-2.cpp ├── 5-3.cpp ├── 5-4.cpp └── test.txt └── 题目 ├── 1-1 前缀平均值.md ├── 1-2 浮点数的分数表达.md ├── 1449550169950.png ├── 2-1 有重复元素的排列问题.md ├── 2-2 统计逆序对.md ├── 2-3 幸运之星(约瑟夫环).md ├── 2-4 整数因子分解.md ├── 2-5 两个有序数序列中找第k小.md ├── 2-6 分治法求众数.md ├── 2-7 圣诞礼物.md ├── 2-8 整数划分的扩展问题.md ├── 2-9 矩阵连乘积的加括号方式数.md ├── 3-1 最大长方体问题.md ├── 3-2 最长上升子序列.md ├── 3-3 最长公共子字符串.md ├── 3-4 数字三角.md ├── 3-5 最大m段乘积和最小m段和.md ├── 3-6 不能移动的石子合并.md ├── 4-1 区间相交问题.md ├── 4-2 可以移动的石子合并.md ├── 5-1 骑士问题.md ├── 5-2 子集和问题.md ├── 5-3工作分配问题.md ├── 5-4 多机最佳调度.md └── 提示.md /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 算法练习 3 | 4 | > 个人平时做的练习,仅供参考 5 | 6 | --- 7 | ### 一般基础 8 | 9 | 1-1 [前缀平均值](./题目/1-1 前缀平均值.md) 10 | 11 | 1-2 [浮点数的分数表达](./题目/1-2 浮点数的分数表达.md) 12 | 13 | ### 分治与递归 14 | 15 | 2-1 [有重复元素的排列问题](./题目/2-1 有重复元素的排列问题.md) 16 | 17 | 2-2 [统计逆序对](./题目/2-2 统计逆序对.md) 18 | 19 | 2-3 [幸运之星(约瑟夫环)](./题目/2-3 幸运之星(约瑟夫环).md) 20 | 21 | 2-4 [整数因子分解](./题目/2-4 整数因子分解.md) 22 | 23 | 2-5 [两个有序数序列中找第k小](./题目/2-5 两个有序数序列中找第k小.md) 24 | 25 | 2-6 [分治法求众数](./题目/2-6 分治法求众数.md) 26 | 27 | 2-7 [圣诞礼物](./题目/2-6 圣诞礼物.md) 28 | 29 | 2-8 [整数划分的扩展问题](./题目/2-8 整数划分的扩展问题.md) 30 | 31 | 2-9 [矩阵连乘积的加括号方式数](./题目/2-9 矩阵连乘积的加括号方式数.md) 32 | 33 | ### 动态规划 34 | 35 | 3-1 [最大长方体问题](./题目/3-1 最大长方体问题.md) 36 | 37 | 3-2 [最长上升序列串](./题目/3-2 最长上升子序列.md) 38 | 39 | 3-3 [最长公共子字符串](./题目/3-3 最长公共子字符串.md) 40 | 41 | 3-4 [数字三角](./题目/3-4 数字三角.md) 42 | 43 | 3-5 [最大m段乘积和最小m段和](./题目/3-5 最大m段乘积和最小m段和.md) 44 | 45 | 3-6 [不能移动的石子合并](./题目/3-6 不能移动的石子合并.md) 46 | 47 | ### 贪心算法 48 | 49 | 4-1 [区间相交问题](./题目/4-1 区间相交问题.md) 50 | 51 | 4-2 [可以移动的石子合并](./题目/4-2 可以移动的石子合并.md) 52 | 53 | ### 搜索算法 54 | 55 | 5-1 [骑士问题](./题目/5-1 骑士问题.md) 56 | 57 | 5-2 [子集和问题](./题目/5-2 子集和问题.md) 58 | 59 | 5-3 [工作分配问题](./题目/5-3 工作分配问题.md) 60 | 61 | 5-4 [多机最佳调度](./题目/5-4 多机最佳调度.md) 62 | 63 | --- 64 | 65 | [提示](./题目/提示.md) -------------------------------------------------------------------------------- /leecode/104. Maximum Depth of Binary Tree.md: -------------------------------------------------------------------------------- 1 | # 104. Maximum Depth of Binary Tree 2 | 3 | > Given a binary tree, find its maximum depth. 4 | The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 5 | 6 | 7 | 8 | Answer: 9 | 10 | ``` 11 | /** 12 | * Definition for a binary tree node. 13 | * function TreeNode(val) { 14 | * this.val = val; 15 | * this.left = this.right = null; 16 | * } 17 | */ 18 | /** 19 | * @param {TreeNode} root 20 | * @return {number} 21 | */ 22 | var maxDepth = function(root) { 23 | if (!root) { 24 | return 0; 25 | } 26 | return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); 27 | }; 28 | ``` -------------------------------------------------------------------------------- /leecode/168. Excel Sheet Column Title.md: -------------------------------------------------------------------------------- 1 | # 168. Excel Sheet Column Title 2 | 3 | > Given a positive integer, return its corresponding column title as appear in an Excel sheet. 4 | For example: 5 | 1 -> A 6 | 2 -> B 7 | 3 -> C 8 | ... 9 | 26 -> Z 10 | 27 -> AA 11 | 28 -> AB 12 | 13 | 14 | 15 | Answer: 16 | 17 | ``` 18 | /** 19 | * @param {number} n 20 | * @return {string} 21 | */ 22 | var convertToTitle = function(n) { 23 | var ret = ""; 24 | while(n) { 25 | ret = String.fromCharCode((n-1)%26 + 65) + ret; 26 | n = Math.floor((n-1)/26); 27 | } 28 | return ret; 29 | }; 30 | ``` -------------------------------------------------------------------------------- /leecode/231. Power of Two.md: -------------------------------------------------------------------------------- 1 | # 231. Power of Two 2 | 3 | > Given an integer, write a function to determine if it is a power of two. 4 | 5 | 6 | Answer: 7 | 8 | ``` 9 | /** 10 | * @param {number} n 11 | * @return {boolean} 12 | */ 13 | var isPowerOfTwo = function(n) { 14 | return n > 0 ? (n & (n - 1)) === 0 : false; 15 | }; 16 | ``` -------------------------------------------------------------------------------- /leecode/292.Nim Game.md: -------------------------------------------------------------------------------- 1 | # 292. Nim Game 2 | 3 | > You are playing the following Nim Game with your friend: There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn to remove the stones. 4 | 5 | > Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win the game given the number of stones in the heap. 6 | 7 | > For example, if there are 4 stones in the heap, then you will never win the game: no matter 1, 2, or 3 stones you remove, the last stone will always be removed by your friend. 8 | 9 | 10 | 11 | Answer: 12 | 13 | ``` 14 | /** 15 | * @param {number} n 16 | * @return {boolean} 17 | */ 18 | var canWinNim = function(n) { 19 | return (n & 3) > 0; 20 | } 21 | ``` -------------------------------------------------------------------------------- /leecode/342.Power of Four.md: -------------------------------------------------------------------------------- 1 | # 344. Reverse String 2 | 3 | > Given an integer (signed 32 bits), write a function to check whether it is a power of 4. 4 | 5 | Example: 6 | Given num = 16, return true. Given num = 5, return false. 7 | 8 | Follow up: Could you solve it without loops/recursion? 9 | 10 | Credits: 11 | Special thanks to @yukuairoy for adding this problem and creating all test cases. 12 | 13 | 14 | 15 | Answer: 16 | 17 | ``` 18 | /** 19 | * @param {number} num 20 | * @return {boolean} 21 | */ 22 | var isPowerOfFour = function(num) { 23 | return (num > 0) && (num & (num - 1)) === 0 && ((num & 0x55555555) > 0); 24 | }; 25 | ``` -------------------------------------------------------------------------------- /leecode/344.Reverse String.md: -------------------------------------------------------------------------------- 1 | # 344. Reverse String 2 | 3 | > Write a function that takes a string as input and returns the string reversed. 4 | Example: 5 | Given s = "hello", return "olleh". 6 | 7 | 8 | 9 | Answer: 10 | 11 | ``` 12 | /** 13 | * @param {string} s 14 | * @return {string} 15 | */ 16 | var reverseString = function(s) { 17 | if (s.length === 0) { 18 | return ""; 19 | } else { 20 | return s.split("").reverse().join(""); 21 | } 22 | }; 23 | ``` -------------------------------------------------------------------------------- /leecode/349. Intersection of Two Arrays.md: -------------------------------------------------------------------------------- 1 | # 349. Intersection of Two Arrays 2 | 3 | > Given two arrays, write a function to compute their intersection. 4 | Example: 5 | Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. 6 | Note: 7 | Each element in the result must be unique. 8 | The result can be in any order. 9 | 10 | 11 | 12 | Answer: 13 | 14 | ``` 15 | /** 16 | * @param {number[]} nums1 17 | * @param {number[]} nums2 18 | * @return {number[]} 19 | */ 20 | var intersection = function(nums1, nums2) { 21 | var arr = []; 22 | if (nums1.length === 0 || nums2.length === 0) { 23 | return []; 24 | } else { 25 | for (var i = 0; i < nums1.length; i++) { 26 | for (var j = 0; j < nums2.length; j++) { 27 | if (nums1[i] === nums2[j] && arr.indexOf(nums1[i]) === -1) { 28 | arr.push(nums1[i]); 29 | } 30 | } 31 | } 32 | } 33 | return arr; 34 | }; 35 | ``` -------------------------------------------------------------------------------- /源码/1-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | int n; 7 | double s=0; 8 | scanf("%d\n",&n); 9 | double arr[n]; 10 | for (int i = 0; i < n; i++) { 11 | scanf("%lf",&arr[i]); 12 | 13 | } 14 | for (int i = 0; i < n; i++) { 15 | s += arr[i]; 16 | printf("%.2lf ", s / (i+1)); 17 | } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /源码/1-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int dotPos; 7 | int pos; 8 | 9 | //求最大公约数 10 | long long GCD(long long a, long long b) 11 | { 12 | return b == 0 ? a : GCD(b, a%b); 13 | } 14 | 15 | //字符c在str的位置 16 | int getPos(char str[], char c) 17 | { 18 | for (int i = 0; i < strlen(str) - 1; i++) 19 | { 20 | if (str[i] == c) 21 | return i; 22 | } 23 | return false; 24 | } 25 | 26 | //判断小数位是否为纯数字 27 | int isDig(char str[]) 28 | { 29 | for (int i = dotPos + 1; i < strlen(str) - 1; i++){ 30 | if (str[i] < '0' || str[i] > '9') { 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | 37 | long long pow10(long long n) 38 | { 39 | long long sum = 1; 40 | for(int i = 0; i < n; i++ ) { 41 | sum *= 10; 42 | } 43 | return sum; 44 | } 45 | //小数转分数 46 | int change(char str[]) { 47 | long long zi=0, mu, gcd; //分子,分母, 最大公约数 48 | long long intNum = 0; //整数部分 49 | long long norLen = 0, cycLen = 0;//非循环节长度,循环节长度, 50 | //非循环小数 51 | if (isDig(str)) { 52 | norLen = strlen(str) - dotPos - 1; 53 | for (int i = dotPos + 1; i < strlen(str); i++){ 54 | zi = zi * 10 + str[i] - '0'; 55 | } 56 | mu = pow10(norLen); 57 | } 58 | //带循环小数 59 | else { 60 | pos = getPos(str, '('); //括号位置 61 | long long a = 0, b = 0; 62 | if (pos) { 63 | for (int i = dotPos + 1; i < pos; i++) { 64 | a = a * 10 + str[i] - '0'; 65 | norLen ++; 66 | } 67 | for (int i = pos + 1; i < strlen(str) - 1; i++) { 68 | b = b * 10 + ( str[i] - '0' ); 69 | cycLen ++; 70 | } 71 | zi = a * (pow10(cycLen) - 1) + b ; 72 | mu = (pow10(cycLen) - 1)*pow10(norLen); 73 | } 74 | } 75 | 76 | //整数部分处理 77 | for (int i = 0; i < dotPos; i++) { 78 | intNum = intNum * 10 + (str[i] - '0'); 79 | } 80 | if (intNum > 0) { 81 | zi += intNum * mu; 82 | } 83 | gcd = GCD(zi, mu); 84 | printf("%lld ", zi/gcd); 85 | printf("%lld",mu/gcd); 86 | return true; 87 | } 88 | 89 | int main() 90 | { 91 | char num[128]; 92 | scanf("%s", &num); 93 | dotPos = getPos(num,'.'); //小数点位置 94 | change(num); 95 | return 0; 96 | } -------------------------------------------------------------------------------- /源码/2-1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/源码/2-1.cpp -------------------------------------------------------------------------------- /源码/2-2.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/源码/2-2.cpp -------------------------------------------------------------------------------- /源码/2-3.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/源码/2-3.cpp -------------------------------------------------------------------------------- /源码/2-4.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | int countNum = 0; 5 | void count (int num) 6 | { 7 | int i; 8 | for (i = num - 1; i > 1; i--) { 9 | if (num % i == 0) { 10 | countNum ++; 11 | count(num/i); 12 | } 13 | } 14 | } 15 | int main() 16 | { 17 | int num; 18 | scanf("%d",&num); 19 | if(num != 0) { 20 | count(num); 21 | printf("%d\n",countNum+1); 22 | } 23 | return 0; 24 | } -------------------------------------------------------------------------------- /源码/2-5.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/源码/2-5.cpp -------------------------------------------------------------------------------- /源码/2-6.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int MAXCOUNT = 0, //众数重数 5 | NUM = 0; //众数 6 | 7 | void split(int a[],int n, int &l,int &r) 8 | { 9 | int mid = n/2; 10 | for (l = 0; l < n; ++l) { 11 | if (a[l] == a[mid]) 12 | break; 13 | } 14 | for (r = l + 1; r < n; ++r) { 15 | if (a[r] != a[mid]) 16 | break; 17 | } 18 | } 19 | 20 | //求众数 21 | void getMode(int a[], int n) 22 | { 23 | int l,r; 24 | split(a,n,l,r); 25 | int count = r - l; //某一段的众数的重数 26 | 27 | //更新 28 | if(count > MAXCOUNT) { 29 | MAXCOUNT = count; 30 | NUM = a[n/2]; 31 | } 32 | 33 | //左边递归 34 | if (l+1 > MAXCOUNT) { 35 | getMode(a,l+1); 36 | } 37 | //右边递归 38 | if (n-r > MAXCOUNT) { 39 | getMode(a+r,n-r); 40 | } 41 | } 42 | 43 | //排序 44 | void insertSort(int arr[], int n){ 45 | for(int i = 1;i < n;i++){ 46 | int temp = arr[i]; 47 | int j = i - 1; 48 | while(temp < arr[j]){ 49 | arr[j+1] = arr[j]; 50 | j--; 51 | if(j == -1) break; 52 | } 53 | arr[j+1] = temp; 54 | } 55 | } 56 | int main() 57 | { 58 | int n; 59 | scanf("%d",&n); 60 | int *a = new int[n]; 61 | for (int i = 0; i < n; i++) { 62 | scanf("%d",&a[i]); 63 | } 64 | insertSort(a,n); 65 | getMode(a,n); 66 | printf("%d %d",NUM,MAXCOUNT); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /源码/2-7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int huafen(int n, int m) 4 | { 5 | if (m < 1 || n < 1) 6 | return 0; 7 | if (m == 1 || n == 1) 8 | return 1; 9 | if (m > n) 10 | return huafen(n,n); 11 | if (m == n) { 12 | return 1 + huafen(n,m-1); 13 | } 14 | return huafen(n,m-1) + huafen(n-m,m); 15 | 16 | } 17 | int main() 18 | { 19 | int n,m; 20 | scanf("%d %d",&n,&m); 21 | printf("%d",huafen(n,m)); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /源码/2-8.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/源码/2-8.cpp -------------------------------------------------------------------------------- /源码/2-9.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int same[20]; 5 | 6 | int p(int n,int same[]){ 7 | int sum = 0; 8 | 9 | if(n == 1){ 10 | return 1; 11 | } 12 | if(n>1) 13 | for(int i = 1;i 2 | 3 | //最长上升子串 4 | int longSub(int *a, int n) 5 | { 6 | int *b = new int[n]; 7 | int maxSub = 1; 8 | b[0] = 1; 9 | for (int i = 1; i < n; i++) { 10 | b[i] = 1; 11 | for (int j = 0; j < i; j++) { 12 | if (a[i] > a[j] && (b[j] + 1) > b[i]) { 13 | b[i] = b[j] + 1; 14 | } 15 | } 16 | } 17 | 18 | for (int i = 0; i < n; i++) { 19 | if (b[i] > maxSub) { 20 | maxSub = b[i]; 21 | } 22 | } 23 | return maxSub; 24 | } 25 | 26 | int main() 27 | { 28 | int n; 29 | scanf("%d",&n); 30 | while(n != 0) { 31 | int *arr = new int[n]; 32 | for (int i = 0; i < n; i++) { 33 | scanf("%d",&arr[i]); 34 | } 35 | printf("%d\n",longSub(arr,n)); 36 | scanf("%d",&n); 37 | } 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /源码/3-3.cpp: -------------------------------------------------------------------------------- 1 | # include 2 | # include 3 | 4 | using namespace std; 5 | string str1, str2; 6 | 7 | int f( int m, int n) { 8 | if ((m < 0) || (n < 0) || str1[m] != str2[n]) { 9 | return 0; 10 | } else { 11 | return f(m - 1, n - 1) + 1; 12 | } 13 | } 14 | 15 | int main() { 16 | int sum = 0, seat; 17 | cin >> str1; 18 | cin >> str2; 19 | for (int i = str1.length() - 1; i >= 0; i--) 20 | for (int j = str2.length() - 1; j >= 0; j--) 21 | if (f(i, j) >= sum) { 22 | sum = f(i, j); 23 | seat = i; 24 | } 25 | cout << sum << endl; 26 | cout << str1.substr(seat - sum + 1, sum) << endl; 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /源码/3-4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | int main() { 6 | int n; 7 | cin >> n; 8 | int arr[n+1][n+1]; 9 | int res[n+1][n+1]; 10 | for (int i = 1; i <= n; i++) { 11 | for (int j = 1; j <= i; j++) { 12 | cin >> arr[i][j]; 13 | } 14 | } 15 | // 初始化res结果数组 16 | for (int i = 1; i <= n; i++) { 17 | res[n][i] = arr[n][i]; 18 | } 19 | for (int i = n - 1; i >= 1; i--) { 20 | for (int j = 1; j <= i; j++) { // 动规填表 21 | res[i][j] = max(res[i+1][j], res[i+1][j+1]) + arr[i][j]; 22 | } 23 | } 24 | cout << res[1][1] << endl;//最大路径和值 25 | 26 | // 找出靠右路径 27 | int y = 1; 28 | cout << arr[1][y] << " "; 29 | //顶部最大 30 | for (int i = 2; i <= n; i++) { 31 | // 从上往下找,只需要比较 y 和 y+1,相应输出 “最大值下标对应的” 原值 32 | if (res[i][y] <= res[i][y+1]) { 33 | cout << arr[i][y+1] << " "; 34 | y = y+1; //更新y 35 | } else { 36 | cout << arr[i][y] << " "; 37 | } 38 | } 39 | cout << endl; 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /源码/3-4.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | /** 4 | * Created by xiami on 2015/11/15. 5 | */ 6 | public class Main { 7 | public static void main(String[] args) { 8 | Scanner scan = new Scanner(System.in); 9 | int n = scan.nextInt(); 10 | int[][] arr = new int[n][n]; 11 | for (int i = 0; i < n; i++) { 12 | for (int j = 0; j <= i; j++) { 13 | arr[i][j] = scan.nextInt(); 14 | } 15 | } 16 | if (n == 1) { 17 | System.out.println(arr[0][0]); 18 | System.out.println(arr[0][0]); 19 | return; 20 | } else if (n == 2) { 21 | int max = arr[1][0] > arr[1][1] ? arr[1][0] : arr[1][1]; 22 | System.out.println(arr[0][0] + max); 23 | System.out.println(arr[0][0] + " " + max); 24 | return; 25 | } 26 | StringBuilder sb[][] = new StringBuilder[n][n]; 27 | 28 | for (int i = 0; i < n; i++) { 29 | sb[n - 1][i] = new StringBuilder().append(arr[n - 1][i]).append(' '); 30 | } 31 | 32 | for (int i = n - 2; i >= 0; i--) { 33 | for (int j = 0; j <= i; j++) { 34 | if (arr[i + 1][j] > arr[i + 1][j + 1]) { 35 | sb[i][j] = new StringBuilder().append(arr[i][j]).append(' ').append(sb[i + 1][j]); 36 | arr[i][j] += arr[i + 1][j]; 37 | } else { 38 | sb[i][j] = new StringBuilder().append(arr[i][j]).append(' ').append(sb[i + 1][j + 1]); 39 | arr[i][j] += arr[i + 1][j + 1]; 40 | } 41 | } 42 | } 43 | System.out.println(arr[0][0]); 44 | System.out.println(sb[0][0].toString()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /源码/3-6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | int m[100][100]; 5 | 6 | int sum_a(int *a,int i,int j){ 7 | int sum = 0,k; 8 | if(i > j) 9 | return 0; 10 | for(k = i; k <= j; k++){ 11 | sum += a[k]; 12 | } 13 | return sum; 14 | } 15 | 16 | void combin_stone1(int *a,int n){ 17 | //最低得分 18 | for (int i = n-2; i >= 0; i--){ 19 | for (int j = i + 1; j < n; j++){ 20 | int temp = m[i][i]+m[i+1][j]; 21 | for (int k = i; k < j; k++){ 22 | temp = min((m[i][k] + m[k+1][j]),temp); 23 | } 24 | m[i][j] = temp + sum_a(a,i,j); 25 | } 26 | } 27 | 28 | cout << m[0][n-1]; 29 | 30 | //最高得分 31 | for(int i = n-2; i >= 0; i--){ 32 | for(int j = i + 1; j < n; j++){ 33 | int temp = m[i][i] + m[i+1][j]; 34 | for(int k = i; k < j; k++){ 35 | temp = max(temp,m[i][k]+m[k+1][j]); 36 | } 37 | m[i][j] = temp + sum_a(a,i,j); 38 | } 39 | } 40 | cout<<" " << m[0][n-1] << endl; 41 | } 42 | 43 | void combin_stone2(int *a,int n) { 44 | int **m=new int*[2*n-1],*b=new int[2*n-1],i,j,k,temp,n1; 45 | n1=2*n-1; 46 | for(i=0;i=0;i--){ 52 | j=i+1; 53 | while(j(m[i][k]+m[k+1][j])) 57 | temp=m[i][k]+m[k+1][j]; 58 | } 59 | m[i][j]=temp+sum_a(b,i,j); 60 | j++; 61 | } 62 | } 63 | 64 | temp=m[0][n-1]; 65 | for(i=0;im[i][n+i-1]) temp=m[i][n+i-1]; 67 | } 68 | cout<=0;i--){ 71 | j=i+1; 72 | while(j> n; 95 | int *a = new int[n]; 96 | for(int i = 0; i < n; i++){ 97 | cin >> a[i]; 98 | m[i][i] = 0; 99 | } 100 | 101 | combin_stone1(a,n); 102 | combin_stone2(a,n); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /源码/4-1.cpp: -------------------------------------------------------------------------------- 1 | # include 2 | # include 3 | using namespace std; 4 | 5 | struct val //区间 6 | { 7 | int l, r; //左右数值 8 | }p[50]; 9 | 10 | bool cmp(struct val a, struct val b) 11 | { 12 | return a.r < b.r; 13 | } 14 | 15 | int main() 16 | { 17 | int n; //区间数 18 | scanf("%d",&n); 19 | 20 | //输入区间,左小右大 21 | for (int i = 0; i < n; i++) { 22 | int l,r; 23 | scanf("%d%d",&l,&r); 24 | if (l <= r) { 25 | p[i].l = l; 26 | p[i].r = r; 27 | } else { 28 | p[i].l = r; 29 | p[i].r = l; 30 | } 31 | } 32 | 33 | sort(p,p+n,cmp); //按右端点升序排列 34 | 35 | int cnt = 1, //最大相容数目 36 | limit = p[0].r; 37 | for (int i = 1; i < n; i++) { 38 | if (p[i].l >= limit) { //贪心选择满足相容性且终点最小的区间 39 | cnt ++; 40 | limit = p[i].r; 41 | } 42 | } 43 | cnt = n - cnt; 44 | printf("%d",cnt); 45 | return 0; 46 | } -------------------------------------------------------------------------------- /源码/4-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define REP(i, n) for(int i = 0; i < (n); ++i) 8 | #define FOR(i, a, b) for(int i = (a); i <= (b); ++i) 9 | using namespace std; 10 | const int N = 110; 11 | int n, m, a[N]; 12 | 13 | priority_queue, greater > qmi; 14 | priority_queue, less >qmx; 15 | 16 | int main() 17 | { 18 | 19 | int n, k; 20 | while(scanf("%d%d", &n, &k) > 0) { 21 | REP(i, n) { 22 | scanf("%d", a + i); 23 | qmi.push(a[i]); 24 | qmx.push(a[i]); 25 | } 26 | 27 | int ans = 0; 28 | if(n > k) { 29 | int t = (n - k) % (k - 1); 30 | if(t) { 31 | while(!qmi.empty() && t >= 0) { 32 | ans += qmi.top(); 33 | qmi.pop(); 34 | --t; 35 | } 36 | if(!qmi.empty()) qmi.push(ans); 37 | } 38 | } 39 | while(true) { 40 | int a = 0; 41 | for(int i = 0; !qmi.empty() && i < k; ++i) { 42 | a += qmi.top(); 43 | qmi.pop(); 44 | } 45 | ans += a; 46 | if(qmi.empty()) break; 47 | qmi.push(a); 48 | } 49 | printf("%d ", ans); 50 | 51 | ans = 0; 52 | while(true) { 53 | int a = 0; 54 | for(int i = 0; i < 2 && !qmx.empty(); ++i) { 55 | a += qmx.top(); 56 | qmx.pop(); 57 | } 58 | ans += a; 59 | if(qmx.empty()) break; 60 | qmx.push(a); 61 | } 62 | printf("%d\n", ans); 63 | } 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /源码/5-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | typedef struct point 6 | { 7 | int x; 8 | int y; 9 | int dep; 10 | }point; 11 | int board[12][12]; //棋盘,-1为障碍,0为通路 12 | int p[][2]={{-1,-2},{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2}}; 13 | point start, end; 14 | //棋盘初始化 15 | void initBoard() 16 | { 17 | for (int i = 0; i < 12; i++) { 18 | for (int j = 0; j < 12; j++) { 19 | board[i][j] = -1; 20 | } 21 | } 22 | //除去两个的围栏其他都为通路 23 | for (int i = 2; i < 10; i++) { 24 | for (int j = 2; j < 10; j++) { 25 | board[i][j] = 0; 26 | } 27 | } 28 | } 29 | 30 | int bfs() 31 | { 32 | queue Q; //存储跳过的点队列 33 | Q.push(start); 34 | while (!Q.empty()) { 35 | // 从起点开始,检查该位置所能跳的周围8个位置 36 | for (int i = 0; i < 8; i++) { 37 | point cur = Q.front(); 38 | //通路 39 | if (board[cur.x + p[i][0]][cur.y + p[i][1]] == 0) { 40 | //到达终点 41 | if (cur.x + p[i][0] == end.x && cur.y + p[i][1] == end.y) { 42 | end.dep = cur.dep + 1; 43 | return end.dep; 44 | } 45 | board[cur.x + p[i][0]][cur.y + p[i][1]] = 1; 46 | point temp; 47 | temp.x = cur.x + p[i][0]; 48 | temp.y = cur.y + p[i][1]; 49 | temp.dep = cur.dep + 1; 50 | Q.push(temp); 51 | } 52 | } 53 | Q.pop(); 54 | } 55 | return 0; 56 | } 57 | 58 | int main() 59 | { 60 | int b, n = 1; //障碍物格子数,board的个数 61 | while(cin >> b && b != -1) { 62 | initBoard(); 63 | //输入障碍 64 | string a; 65 | for (int i = 0; i < b; i++) { 66 | cin >> a; 67 | board[a[0]-'a'+2][a[1]-'1'+2] = -1; 68 | } 69 | //起点 70 | cin >> a; 71 | start.x = a[0]-'a'+2; 72 | start.y = a[1]-'1'+2; 73 | start.dep = 0; 74 | //终点 75 | cin >> a; 76 | end.x = a[0]-'a'+2; 77 | end.y = a[1]-'1'+2; 78 | 79 | int dep = bfs(); 80 | if (dep == 0) { 81 | cout << "board " << n++ << ":" << "not reachable" << endl; 82 | } else { 83 | cout << "board " << n++ << ":" << dep << " moves" << endl; 84 | } 85 | 86 | } 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /源码/5-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int n, c; 7 | int sum = 0; 8 | int leaf = 0; 9 | 10 | void getSubSum(int i,int *ary,int *x){ 11 | 12 | if(i >= n){ 13 | leaf++; //叶子节点+1 14 | if(sum == c) { 15 | for(int i = 0; i < n; i++){ 16 | if(x[i]==1) 17 | cout<> n >> c; 38 | int *x = new int[n]; //由于只输出遇到的第一个解,所以此处只设置一个解向量 39 | int *a = new int[n]; 40 | for (int i = 0; i < n; i++) { 41 | cin >> a[i]; 42 | } 43 | getSubSum(0,a,x); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /源码/5-3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int n, //工作量n 6 | cost = 0, //总花费 7 | x[100] = {0}, //X[j],第j号工作是否被分配,0表示没有,1表示有 8 | worker1[100],worker2[100], //表示第1个工作分配给第2个人,第2个工作分配给第1个人,第3个工作分配给第3个人 9 | c[100][100]; //c[i][j] i号工人完成j号工作的价钱 10 | 11 | //sum:费用总和 12 | //i:第i号工人 13 | void work(int i, int sum) 14 | { 15 | if (i > n && sum < cost) { 16 | cost = sum; 17 | for (int i = 1; i <= n; i++) { 18 | worker2[i] = worker1[i]; 19 | } 20 | return; 21 | } 22 | for (int j = 1; j <= n; j++) { 23 | if (x[j] == 0) { 24 | x[j] = i; 25 | worker1[i] = j; //第i个人的工作为j; 26 | //cout << "工作" << j << "被分给第" << i <<"人" << endl; 27 | work(i+1, sum + c[i][j]); 28 | x[j] = 0; 29 | } 30 | } 31 | } 32 | 33 | int main() 34 | { 35 | cin >> n; 36 | 37 | for (int i = 1; i <= n; i++) { 38 | for (int j = 1; j <= n; j++) { 39 | cin >> c[i][j]; 40 | } 41 | cost += c[i][i]; 42 | worker2[i] = i; 43 | } 44 | work(1,0); 45 | cout << cost << endl; 46 | for (int i = 1; i<=n ; i++) { 47 | cout << worker2[i] << " "; 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /源码/5-4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | int n,m; //n个任务,m台机器 6 | 7 | int x[100]; 8 | int bestx[100]; 9 | int best=0; 10 | 11 | bool cmp(int a,int b) 12 | { 13 | return a > b; 14 | } 15 | 16 | void printArr(int *arr, int n) 17 | { 18 | for (int i = 0; i < n; i++) { 19 | cout << arr[i] << " "; 20 | } 21 | cout << endl; 22 | } 23 | 24 | //贪心算法 25 | void distribute_1(int *tasktime, int *mtime) 26 | { 27 | sort(tasktime,tasktime+n,cmp); 28 | if (n <= m) { //任务数量小于机器数 29 | cout << tasktime[0] << endl; 30 | } else { //把任务给空闲的机器 31 | for (int i = 0; i < n; i++) { 32 | //printArr(mintime,m); 33 | *min_element(mtime, mtime+m) += tasktime[i]; 34 | } 35 | cout << *max_element(mtime, mtime + m) << endl; 36 | } 37 | } 38 | 39 | //mtime是个数组:m个元素对应m台机的空闲时间,即第二种回溯算法在搜索过程中已探察过任务且针对某台机的完成时间和; 40 | 41 | //回溯算法 42 | void distribute_2(int dep, int *tasktime, int *mtime) 43 | { 44 | if (dep == n) //叶子 45 | { 46 | int temp = *max_element(mtime, mtime + m); //计算走该路径所需时间 47 | if (temp < best) { //更新最小时间和最小时间路径 48 | best = temp; 49 | } 50 | return; 51 | } 52 | 53 | for(int i = 0; i < m; i++) 54 | { 55 | mtime[i] += tasktime[dep]; 56 | if(mtime[i] < best) { // 57 | distribute_2(dep+1, tasktime, mtime); 58 | } 59 | mtime[i] -= tasktime[dep]; 60 | } 61 | 62 | } 63 | 64 | //将机器运行时间默认置0 65 | void initMtime(int *mtime) 66 | { 67 | for (int i = 0; i < m; i++) { 68 | mtime[i] = 0; 69 | } 70 | } 71 | 72 | int main() 73 | { 74 | cin >> n >> m; 75 | int *tasktime = new int[n]; //任务需要的时间 76 | int *mtime = new int[m]; //机器运行时间 77 | initMtime(mtime); 78 | for (int i = 0; i < n; i++) { 79 | cin >> tasktime[i]; 80 | best += tasktime[i]; 81 | } 82 | distribute_1(tasktime, mtime); 83 | initMtime(mtime); 84 | distribute_2(0,tasktime, mtime); 85 | 86 | cout << best << endl; 87 | return 0; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /源码/test.txt: -------------------------------------------------------------------------------- 1 | // 有重复元素排列 2 | #include 3 | #include 4 | #include 5 | 6 | #define TRUE 1 7 | #define FALSE 0 8 | 9 | int total = 0; 10 | inline void Swap(char &a, char &b) 11 | { 12 | char temp = a; 13 | a = b; 14 | b = temp; 15 | } 16 | int isRepeat (char str[], int a, int b) 17 | { 18 | for (int i = a; i < b; i++) { 19 | if (str[i] == str[b]) 20 | return FALSE; 21 | } 22 | return TRUE; 23 | } 24 | int Perm(char list[], int k, int m) 25 | { 26 | 27 | //产生list[k:m]的所有排列 28 | 29 | if (k == m) { 30 | for (int i = 0; i <= m; i++) { 31 | printf("%c",list[i]); 32 | } 33 | printf("\n"); 34 | total ++; 35 | } 36 | else { 37 | for (int i = k; i <= m; i++) { 38 | if (isRepeat(list, k, i)) { 39 | Swap(list[k], list[i]); 40 | Perm(list, k+1, m); 41 | Swap(list[k],list[i]); 42 | } 43 | 44 | } 45 | } 46 | return total; 47 | } 48 | 49 | int main() 50 | { 51 | int LENGTH = 0,i; 52 | scanf("%d\n",&LENGTH); //元素个数: >=1 && <=15 53 | char str[LENGTH]; 54 | for (i = 0; i < LENGTH; i++) { 55 | scanf("%c",&str[i]); 56 | } 57 | printf("%d\n",Perm(str,0,LENGTH-1)); 58 | return 0; 59 | } 60 | 61 | // 整数因子分解 62 | 63 | #include 64 | #include 65 | int countNum = 0; 66 | void count (int num) 67 | { 68 | int i; 69 | for (i = num - 1; i > 1; i--) { 70 | if (num % i == 0) { 71 | countNum ++; 72 | count(num/i); 73 | } 74 | } 75 | } 76 | int main() 77 | { 78 | int num; 79 | scanf("%d",&num); 80 | if(num != 0) { 81 | count(num); 82 | printf("%d\n",countNum+1); 83 | } 84 | return 0; 85 | } 86 | 87 | // 找第k小 88 | #include 89 | #include 90 | 91 | #define MAXSIZE 10000 92 | 93 | int x[MAXSIZE],y[MAXSIZE],k; 94 | 95 | int fun(int xBeg, int xEnd, int yBeg, int yEnd) 96 | { 97 | int xMid = xBeg + ( xEnd - xBeg ) / 2, 98 | yMid = yBeg + ( yEnd - yBeg ) / 2, 99 | lMid = xMid - xBeg + yMid - yBeg + 2; //X左段和Y左段元素个数合计 100 | if (xBeg > xEnd) 101 | return y[yBeg + k - 1]; //X序列为空时,直接返回Y序列的第k小元素 102 | if (yBeg > yEnd) 103 | return x[xBeg + k - 1]; 104 | 105 | if (x[xMid] < y[yMid]) { 106 | if (k < lMid) { 107 | return fun(xBeg, xEnd, yBeg, yMid - 1); 108 | } 109 | else { 110 | k = k - ( xMid - xBeg + 1); 111 | return fun(xMid + 1, xEnd, yBeg, yEnd); 112 | } 113 | } 114 | else { 115 | if (k < lMid) { 116 | return fun(xBeg, xMid - 1, yBeg, yEnd); 117 | } 118 | else { 119 | k = k - (yMid - yBeg + 1); 120 | return fun(xBeg, xEnd, yMid + 1, yEnd); 121 | } 122 | } 123 | 124 | 125 | } 126 | 127 | int main() 128 | { 129 | int m,n,i; 130 | scanf("%d %d %d",&m,&n,&k); 131 | for (i = 0; i < m; i++) { 132 | scanf("%d",&x[i]); 133 | } 134 | for (i = 0; i < n; i++) { 135 | scanf("%d",&y[i]); 136 | } 137 | printf("%d",fun(0,m-1,0,n-1)); 138 | return 0; 139 | } 140 | 141 | // 整数划分拓展问题 142 | #include 143 | 144 | int Odd(int i,int j); 145 | int Even(int i, int j); 146 | //Even(I,J)表示I划分为J个正偶数的划分数. 147 | int Even(int i,int j) 148 | { 149 | if (j > i || j <= 0) 150 | return 0; 151 | if ((i == 1 && j == 1) || (i == 2 && j == 2)) 152 | return 0; 153 | if (i == 2 && j == 1) 154 | return 1; 155 | return Odd(i-j,j); //每个偶加数减掉1变为奇加数 156 | } 157 | //Odd(I,J)表示I划分为J个正奇数的划分数 158 | int Odd(int i, int j) 159 | { 160 | 161 | if ((i == 1 && j == 1)||(i == 2 && j == 2)) 162 | return 1; 163 | if (i == 2 && j == 1) 164 | return 0; 165 | if (j > i || j < 0) 166 | return 0; 167 | return Odd(i-1,j-1) + Even(i-j,j); //加数含1的方式数 + 加数不含1的方式数 168 | } 169 | 170 | 171 | int huafen1(int n, int m) 172 | { 173 | if (m < 1 || n < 1) 174 | return 0; 175 | if (m == 1 || n == 1) 176 | return 1; 177 | if (m > n) 178 | return huafen1(n,n); 179 | if (m == n) { 180 | return 1 + huafen1(n,m-1); 181 | } 182 | return huafen1(n,m-1) + huafen1(n-m,m); 183 | 184 | } 185 | int main() 186 | { 187 | int n,m; 188 | int i = 0,res1 = 0, res2 = 0; 189 | scanf("%d %d",&n,&m); 190 | res1 = huafen1(n,m); 191 | for (i = 1; i <= n; i++) { 192 | res2 += Odd(n,i); 193 | } 194 | printf("%d %d %d %d", res1,res1,res2,res2); 195 | return 0; 196 | } 197 | 198 | // 最长上升子序列 199 | #include 200 | 201 | //最长上升子串 202 | int longSub(int *a, int n) 203 | { 204 | int *b = new int[n]; 205 | int maxSub = 1; 206 | b[0] = 1; 207 | for (int i = 1; i < n; i++) { 208 | b[i] = 1; 209 | for (int j = 0; j < i; j++) { 210 | if (a[i] > a[j] && (b[j] + 1) > b[i]) { 211 | b[i] = b[j] + 1; 212 | } 213 | } 214 | } 215 | 216 | for (int i = 0; i < n; i++) { 217 | if (b[i] > maxSub) { 218 | maxSub = b[i]; 219 | } 220 | } 221 | return maxSub; 222 | } 223 | 224 | int main() 225 | { 226 | int n; 227 | scanf("%d",&n); 228 | while(n != 0) { 229 | int *arr = new int[n]; 230 | for (int i = 0; i < n; i++) { 231 | scanf("%d",&arr[i]); 232 | } 233 | printf("%d\n",longSub(arr,n)); 234 | scanf("%d",&n); 235 | } 236 | return 0; 237 | } 238 | 239 | // 最长上升子字符串 240 | /# include 241 | # include 242 | 243 | using namespace std; 244 | string str1, str2; 245 | 246 | int f( int m, int n) { 247 | if ((m < 0) || (n < 0) || str1[m] != str2[n]) { 248 | return 0; 249 | } else { 250 | return f(m - 1, n - 1) + 1; 251 | } 252 | } 253 | 254 | int main() { 255 | int sum = 0, seat; 256 | cin >> str1; 257 | cin >> str2; 258 | for (int i = str1.length() - 1; i >= 0; i--) 259 | for (int j = str2.length() - 1; j >= 0; j--) 260 | if (f(i, j) >= sum) { 261 | sum = f(i, j); 262 | seat = i; 263 | } 264 | cout << sum << endl; 265 | cout << str1.substr(seat - sum + 1, sum) << endl; 266 | } 267 | 268 | 269 | 270 | // 数字三角 271 | import java.util.Scanner; 272 | 273 | /** 274 | * Created by xiami on 2015/11/15. 275 | */ 276 | public class Main { 277 | public static void main(String[] args) { 278 | Scanner scan = new Scanner(System.in); 279 | int n = scan.nextInt(); 280 | int[][] arr = new int[n][n]; 281 | for (int i = 0; i < n; i++) { 282 | for (int j = 0; j <= i; j++) { 283 | arr[i][j] = scan.nextInt(); 284 | } 285 | } 286 | if (n == 1) { 287 | System.out.println(arr[0][0]); 288 | System.out.println(arr[0][0]); 289 | return; 290 | } else if (n == 2) { 291 | int max = arr[1][0] > arr[1][1] ? arr[1][0] : arr[1][1]; 292 | System.out.println(arr[0][0] + max); 293 | System.out.println(arr[0][0] + " " + max); 294 | return; 295 | } 296 | StringBuilder sb[][] = new StringBuilder[n][n]; 297 | 298 | for (int i = 0; i < n; i++) { 299 | sb[n - 1][i] = new StringBuilder().append(arr[n - 1][i]).append(' '); 300 | } 301 | 302 | for (int i = n - 2; i >= 0; i--) { 303 | for (int j = 0; j <= i; j++) { 304 | if (arr[i + 1][j] > arr[i + 1][j + 1]) { 305 | sb[i][j] = new StringBuilder().append(arr[i][j]).append(' ').append(sb[i + 1][j]); 306 | arr[i][j] += arr[i + 1][j]; 307 | } else { 308 | sb[i][j] = new StringBuilder().append(arr[i][j]).append(' ').append(sb[i + 1][j + 1]); 309 | arr[i][j] += arr[i + 1][j + 1]; 310 | } 311 | } 312 | } 313 | System.out.println(arr[0][0]); 314 | System.out.println(sb[0][0].toString()); 315 | } 316 | } 317 | 318 | // 不能移动石子合并 319 | #include 320 | 321 | using namespace std; 322 | int m[100][100]; 323 | 324 | int sum_a(int *a,int i,int j){ 325 | int sum = 0,k; 326 | if(i > j) 327 | return 0; 328 | for(k = i; k <= j; k++){ 329 | sum += a[k]; 330 | } 331 | return sum; 332 | } 333 | 334 | void combin_stone1(int *a,int n){ 335 | //最低得分 336 | for (int i = n-2; i >= 0; i--){ 337 | for (int j = i + 1; j < n; j++){ 338 | int temp = m[i][i]+m[i+1][j]; 339 | for (int k = i; k < j; k++){ 340 | temp = min((m[i][k] + m[k+1][j]),temp); 341 | } 342 | m[i][j] = temp + sum_a(a,i,j); 343 | } 344 | } 345 | 346 | cout << m[0][n-1]; 347 | 348 | //最高得分 349 | for(int i = n-2; i >= 0; i--){ 350 | for(int j = i + 1; j < n; j++){ 351 | int temp = m[i][i] + m[i+1][j]; 352 | for(int k = i; k < j; k++){ 353 | temp = max(temp,m[i][k]+m[k+1][j]); 354 | } 355 | m[i][j] = temp + sum_a(a,i,j); 356 | } 357 | } 358 | cout<<" " << m[0][n-1] << endl; 359 | } 360 | 361 | void combin_stone2(int *a,int n) { 362 | int **m=new int*[2*n-1],*b=new int[2*n-1],i,j,k,temp,n1; 363 | n1=2*n-1; 364 | for(i=0;i=0;i--){ 370 | j=i+1; 371 | while(j(m[i][k]+m[k+1][j])) 375 | temp=m[i][k]+m[k+1][j]; 376 | } 377 | m[i][j]=temp+sum_a(b,i,j); 378 | j++; 379 | } 380 | } 381 | 382 | temp=m[0][n-1]; 383 | for(i=0;im[i][n+i-1]) temp=m[i][n+i-1]; 385 | } 386 | cout<=0;i--){ 389 | j=i+1; 390 | while(j> n; 413 | int *a = new int[n]; 414 | for(int i = 0; i < n; i++){ 415 | cin >> a[i]; 416 | m[i][i] = 0; 417 | } 418 | 419 | combin_stone1(a,n); 420 | combin_stone2(a,n); 421 | return 0; 422 | } 423 | 424 | // 区间相交 425 | # include 426 | # include 427 | using namespace std; 428 | 429 | struct val //区间 430 | { 431 | int l, r; //左右数值 432 | }p[50]; 433 | 434 | bool cmp(struct val a, struct val b) 435 | { 436 | return a.r < b.r; 437 | } 438 | 439 | int main() 440 | { 441 | int n; //区间数 442 | scanf("%d",&n); 443 | 444 | //输入区间,左小右大 445 | for (int i = 0; i < n; i++) { 446 | int l,r; 447 | scanf("%d%d",&l,&r); 448 | if (l <= r) { 449 | p[i].l = l; 450 | p[i].r = r; 451 | } else { 452 | p[i].l = r; 453 | p[i].r = l; 454 | } 455 | } 456 | 457 | sort(p,p+n,cmp); //按右端点升序排列 458 | 459 | int cnt = 1, //最大相容数目 460 | limit = p[0].r; 461 | for (int i = 1; i < n; i++) { 462 | if (p[i].l >= limit) { //贪心选择满足相容性且终点最小的区间 463 | cnt ++; 464 | limit = p[i].r; 465 | } 466 | } 467 | cnt = n - cnt; 468 | printf("%d",cnt); 469 | return 0; 470 | } 471 | // 可移动石子合并 472 | #include 473 | #include 474 | #include 475 | #include 476 | #include 477 | #include 478 | #define REP(i, n) for(int i = 0; i < (n); ++i) 479 | #define FOR(i, a, b) for(int i = (a); i <= (b); ++i) 480 | using namespace std; 481 | const int N = 110; 482 | int n, m, a[N]; 483 | 484 | priority_queue, greater > qmi; 485 | priority_queue, less >qmx; 486 | 487 | int main() 488 | { 489 | 490 | int n, k; 491 | while(scanf("%d%d", &n, &k) > 0) { 492 | REP(i, n) { 493 | scanf("%d", a + i); 494 | qmi.push(a[i]); 495 | qmx.push(a[i]); 496 | } 497 | 498 | int ans = 0; 499 | if(n > k) { 500 | int t = (n - k) % (k - 1); 501 | if(t) { 502 | while(!qmi.empty() && t >= 0) { 503 | ans += qmi.top(); 504 | qmi.pop(); 505 | --t; 506 | } 507 | if(!qmi.empty()) qmi.push(ans); 508 | } 509 | } 510 | while(true) { 511 | int a = 0; 512 | for(int i = 0; !qmi.empty() && i < k; ++i) { 513 | a += qmi.top(); 514 | qmi.pop(); 515 | } 516 | ans += a; 517 | if(qmi.empty()) break; 518 | qmi.push(a); 519 | } 520 | printf("%d ", ans); 521 | 522 | ans = 0; 523 | while(true) { 524 | int a = 0; 525 | for(int i = 0; i < 2 && !qmx.empty(); ++i) { 526 | a += qmx.top(); 527 | qmx.pop(); 528 | } 529 | ans += a; 530 | if(qmx.empty()) break; 531 | qmx.push(a); 532 | } 533 | printf("%d\n", ans); 534 | } 535 | return 0; 536 | } 537 | 538 | // 子集合 539 | #include 540 | #include 541 | #include 542 | using namespace std; 543 | 544 | int n, c; 545 | int sum = 0; 546 | int leaf = 0; 547 | 548 | void getSubSum(int i,int *ary,int *x){ 549 | 550 | if(i >= n){ 551 | leaf++; //叶子节点+1 552 | if(sum == c) { 553 | for(int i = 0; i < n; i++){ 554 | if(x[i]==1) 555 | cout<> n >> c; 576 | int *x = new int[n]; //由于只输出遇到的第一个解,所以此处只设置一个解向量 577 | int *a = new int[n]; 578 | for (int i = 0; i < n; i++) { 579 | cin >> a[i]; 580 | } 581 | getSubSum(0,a,x); 582 | return 0; 583 | } 584 | 585 | // 工作分配 586 | #include 587 | 588 | using namespace std; 589 | 590 | int n, //工作量n 591 | cost = 0, //总花费 592 | x[100] = {0}, //X[j],第j号工作是否被分配,0表示没有,1表示有 593 | worker1[100],worker2[100], //表示第1个工作分配给第2个人,第2个工作分配给第1个人,第3个工作分配给第3个人 594 | c[100][100]; //c[i][j] i号工人完成j号工作的价钱 595 | 596 | //sum:费用总和 597 | //i:第i号工人 598 | void work(int i, int sum) 599 | { 600 | if (i > n && sum < cost) { 601 | cost = sum; 602 | for (int i = 1; i <= n; i++) { 603 | worker2[i] = worker1[i]; 604 | } 605 | return; 606 | } 607 | for (int j = 1; j <= n; j++) { 608 | if (x[j] == 0) { 609 | x[j] = i; 610 | worker1[i] = j; //第i个人的工作为j; 611 | //cout << "工作" << j << "被分给第" << i <<"人" << endl; 612 | work(i+1, sum + c[i][j]); 613 | x[j] = 0; 614 | } 615 | } 616 | } 617 | 618 | int main() 619 | { 620 | cin >> n; 621 | 622 | for (int i = 1; i <= n; i++) { 623 | for (int j = 1; j <= n; j++) { 624 | cin >> c[i][j]; 625 | } 626 | cost += c[i][i]; 627 | worker2[i] = i; 628 | } 629 | work(1,0); 630 | cout << cost << endl; 631 | for (int i = 1; i<=n ; i++) { 632 | cout << worker2[i] << " "; 633 | } 634 | 635 | return 0; 636 | } 637 | 638 | // 多级最佳调度 -------------------------------------------------------------------------------- /题目/1-1 前缀平均值.md: -------------------------------------------------------------------------------- 1 | #1-1 前缀平均值 2 | --- 3 | ####Description 4 | >数列的前缀平均值(prefix average)问题: 5 | 给定存储n个double型浮点数的数组X,要计算数组A,其中A[i]为元素X[0],…, X[i]的平均值(i=0,…,n-1)(1<=n<=1000000),即前缀平均值在经济学和统计学中广泛应用。 6 | 7 | 8 | 已知长度为n的X[i]序列(i=0,…,n-1),求长度为n的前缀平均值序列A[i] (i=0,…,n-1)。试设计一种求前缀平均值的算法,使得算法的平均运行时间为O(n)。 9 | 10 | *注意:这里,要求设计的算法运行时间是O(n),而非直接通过公式计算的O(n^2)。* 11 | 12 | 13 | 14 | ####输入格式: 15 | 分两行: 16 | 第一行仅一个数,为n,表示接下来有n个浮点数。(n<=1000000) 17 | 第二行共有n个double型浮点数,为原始数X[0] … X[n-1] 18 | 19 | 20 | ####输出格式: 21 | 仅一行共有n个平均值,为前缀平均值A[0] … A[n-1] 22 | 23 | 格式:每个数值仅保留小数点后两位输出,数值之间空格相连。 24 | 25 | ####输入样例 26 | 3 27 | 0.125126 56.3585 19.3304 28 | 29 | 30 | ####输出样例 31 | 0.13 28.24 25.27 32 | 33 | 34 | ---- 35 | 36 | [解答](../源码/1-1.cpp) -------------------------------------------------------------------------------- /题目/1-2 浮点数的分数表达.md: -------------------------------------------------------------------------------- 1 | # 1-2 浮点数的分数表达 2 | --- 3 | 4 | ####Description: 5 | 在计算机中,用float或double来存储小数有时不能得到精确值,若要精确表达一个浮点数的计算结果,最好用分数来表示小数,有限小数或无限循环小数都可以转化为分数,无限循环小数的循环节用括号标记出来。如: 6 | 0.9 = 9/10 7 | 0.(3) = 0.3(3) = 0.3(33) = 1/3 8 | 9 | 当然一个小数可以用好几种分数形式来表示,我们只感兴趣最简的分数形式(即分母最小),如: 10 | 0.3(33) = 1/3 = 3/9 11 | 12 | 因为任何一个数都可以转化为一个整数和一个纯小数之和,整数部分较为简单无需做额外处理,只要将纯小数部分转 13 | 化为分数形式,整数部分的分数部分就很简单了。 14 | 15 | 现在给定一个正的纯小数(这个纯小数为有限小数或无限循环小数),请你以最简分数形式来返回这个纯小数。 16 | 17 | 18 | 19 | ####输入格式: 20 | 给定一个纯小数,若是无限循环小数,用括号标记循环节,输入小数表达不超过100个字符。 21 | 22 | 23 | ####输出格式: 24 | 输出:化为最简分数形式,分子在前,分母在后,中间空格连接。 25 | 26 | 27 | ####输入样例: 28 | 0.3(33) 29 | 30 | 31 | ####输出样例: 32 | 1 3 33 | 34 | 35 | --- 36 | 37 | [解答](../源码/1-2.cpp) -------------------------------------------------------------------------------- /题目/1449550169950.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyseria/Algorithm/7b24d1246bc9e73079a3cbe59d26b2f3978f661a/题目/1449550169950.png -------------------------------------------------------------------------------- /题目/2-1 有重复元素的排列问题.md: -------------------------------------------------------------------------------- 1 | # 2-1 有重复元素的排列问题 2 | 3 | #### Description: 4 | > 设集合R={r1,r2,...,rn}是要进行排列的n个元素,其中r1,r2,...,rn可能相同。 5 | 试着设计一个算法,列出R的所有不同排列。 6 | 即,给定n以及待排的n个可能重复的元素。计算输出n个元素的所有不同排列。 7 | 8 | 9 | 10 | ####输入格式 11 | 第1行是元素个数n,1<=n<=15。接下来的1行是待排列的n个元素,元素中间不要加空格。 12 | 13 | 14 | ####输出格式 15 | 程序运行结束时,将计算输出n个元素的所有不同排列。最后1行中的数是排列总数。 16 | 17 | 18 | 19 | ####输入样例 20 | 4 21 | aacc 22 | 23 | 24 | ####输出样例 25 | 26 | aacc 27 | 28 | acac 29 | 30 | acca 31 | 32 | caac 33 | 34 | caca 35 | 36 | ccaa 37 | 38 | 6 39 | 40 | --- 41 | 42 | 43 | [解答](../源码/2-1.cpp) 44 | -------------------------------------------------------------------------------- /题目/2-2 统计逆序对.md: -------------------------------------------------------------------------------- 1 | # 2-2 统计逆序对 2 | --- 3 | #### Description: 4 | > 设a[0…n-1]是一个包含n个数的数组,若在ia[j],则称(i, j)为a数组的一个逆序对(inversion)。比如 <2,3,8,6,1> 有5个逆序对。 5 | 6 | 一个n个元素序列的逆序对个数由三部分构成: 7 | 1. 它的左半部分逆序对的个数, 8 | 2. 加上右半部分逆序对的个数, 9 | 3. 再加上左半部分元素大于右半部分元素的数量。 10 | 其中前两部分(1)和(2)由递归来实现。要保证算法最后效率O(nlogn),第三部分(3)应该如何实现? 11 | 12 | **此题请勿采用O(n^2)的简单枚举算法来实现** 13 | 14 | ####输入格式 15 | 第一行:n,表示接下来要输入n个元素,n不超过10000。 16 | 第二行:n个元素序列。 17 | 18 | ####输出格式 19 | 逆序对的个数。 20 | 21 | ####输入样例 22 | 5 23 | 2 3 8 6 1 24 | 25 | ####输出样例 26 | 5 27 | 28 | 29 | [little tips] 用归并排序的思想 30 | 31 | 32 | --- 33 | 34 | 35 | [解答](../源码/2-2.cpp) -------------------------------------------------------------------------------- /题目/2-3 幸运之星(约瑟夫环).md: -------------------------------------------------------------------------------- 1 | #2-3 幸运之星(约瑟夫环) 2 | 3 | #####Description 4 | 每年新年派对的最后一个节目就是选出下年的“幸运之星”,有丰厚的大礼包的噢~~。 5 | 所以每位参加派对的人士都摩拳擦掌跃跃欲试。选择的办法是这样约定的: 6 | 7 | >(1)所有参与的人员数n,让n个人一字排开,然后至左向右从1开始报数,凡报到奇数号的全部后退剔除,剩下的人员,又至左向右报数,逢奇剔除,如此不断的递归下去,直至只有一个人为止,这个人就是“幸运之星”。 8 | 9 | >(2)所有参与的人员数n,先随机抽取一个m值(从黑暗小箱中随机摸一个,m可能比n小或相等,也可能大于n),所有 10 | 参与的人员列成环形,然后从位置1开始报数,凡报到m的倍数的人后退剔除,剩下的人员,从刚才位置继续报数,逢m的 11 | 倍数的人剔除,如此不断的递归下去,直至只有一个人为止,这个人就是“幸运之星”。 如:n=8,m=4,如下图所示,幸 12 | 运之星为6号。 13 | 14 | 现在,请你分析上面两种节目方式,若想获得幸运大礼包,应该选哪个初始编号的位置来站? 15 | 16 | *注意此题设置的时限很短,也就不建议你采用队列或循环列表去模拟这个剔除的过程而得到最后的解答.* 17 | 18 | ####输入格式 19 | 输入:n和m(m只和第二个问题有关,与第一个问题无关)。 20 | n和m的范围:n,m <1000000。 21 | 22 | 23 | ####输出格式 24 | 输出:幸运之星游戏未开始剔除之前的最初的编号。(1)题和(2)题的解,中间空格相连。 25 | 26 | 27 | ####输入样例 28 | 8 4 29 | 30 | 31 | ####输出样例 32 | 8 6 33 | 34 | 35 | ---- 36 | 37 | 38 | [解答](../源码/2-3.cpp) -------------------------------------------------------------------------------- /题目/2-4 整数因子分解.md: -------------------------------------------------------------------------------- 1 | 2 | # 2-4 整数因子分解 3 | --- 4 | ####Description: 5 | >大于1的正整数 n 都可以分解为 n = x1 * x2 * ... * xm, 每个xi为大于1的因子,即1已知两个已经排好序(非减序)的序列X和Y,其中X的长度为m,Y长度为n, 5 | 现在请你用分治算法,找出X和Y的第k小的数,算法时间复杂度为O(max{logm, logn})。 6 | 7 | *此题请勿采用将序列X和Y合并找第k小的O(m+n)的一般方法,要充分利用X和Y已经排好序的这一特性。* 8 | 9 | 10 | 11 | ####输入格式: 12 | 第一行有三个数,分别是长度m、长度n和k,中间空格相连(1<=m,n<=100000; 1<=k<=m+n)。 13 | 第二行m个数分别是非减序的序列X。第三行n个数分别是非减序的序列Y。 14 | 15 | 16 | ####输出格式: 17 | 序列X和Y的第k小的数。 18 | 19 | 20 | ####输入样例: 21 | 5 6 7 22 | 23 | 1 8 12 12 21 24 | 25 | 4 12 20 22 26 31 26 | 27 | 28 | ####输出样例: 29 | 20 30 | 31 | 32 | --- 33 | 34 | 35 | [解答](../源码/2-5.cpp) -------------------------------------------------------------------------------- /题目/2-6 分治法求众数.md: -------------------------------------------------------------------------------- 1 | #2-6 分治法求众数 2 | 3 | #### Description 4 | >给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。例如,S={1,2,2,2,3,5}。多重集S的众数是2,其重数为3。 5 | 6 | 7 | *编程任务:对于给定的由n个自然数组成的多重集S,采用分治算法编程计算S的众数及其重数。* 8 | 9 | 10 | 11 | #### 输入格式 12 | 第1行多重集S中元素个数n;接下来的一行为集合S,有n个自然数。( n < 100000 ) 13 | 14 | 15 | ####输出格式 16 | 结果输出:输出2个数,第1个数为众数,第2个为其重数。 17 | 当有多个同样重数的众数,优先输出数值更小的众数。 18 | 19 | 20 | ####输入样例 21 | 6 22 | 1 2 2 2 3 5 23 | 24 | 25 | ####输出样例 26 | 2 3 27 | 28 | --- 29 | 30 | 31 | [解答](../源码/2-6.cpp) -------------------------------------------------------------------------------- /题目/2-7 圣诞礼物.md: -------------------------------------------------------------------------------- 1 | # 2-7 圣诞礼物 2 | --- 3 | ###Description: 4 | 圣诞节到了,圣诞老人给 N 个小朋友准备了 M 个礼物。每个小朋友有一个袜子(袜子不编号,无区别, 5 | 认为袜子都相同), 6 | 圣诞老人将 M 个礼物装到 N 个袜子中的放法有多少种? 7 | 8 | 注意: 9 | 10 | 1)若M=7 N=3,那么5,1,1的放法和1,5,1的放法算是同一种装法。 11 | 12 | 2)允许袜子为空。 13 | 14 | 3)M和N无大小关系,M可以比N大,M也可以比N小。 15 | 16 | 17 | 18 | 19 | ###输入格式: 20 | 输入数据包含两个整数 M,N。1<=M,N<=50。 21 | M在前,N在后,中间空格。 22 | 23 | 24 | ###输出格式: 25 | 输出共有几种不同的放法。 26 | 27 | 28 | ###输入样例 29 | 7 3 30 | 31 | 32 | ###输出样例 33 | 8 34 | 35 | 36 | --- 37 | 38 | [解答](../源码/2-7.cpp) -------------------------------------------------------------------------------- /题目/2-8 整数划分的扩展问题.md: -------------------------------------------------------------------------------- 1 | #2-8 整数划分的扩展问题 2 | --- 3 | 4 | ###Description: 5 | 下面有整数划分问题扩展出的多个题例: 6 | (1)正整数n划分为若干正整数之和,最大加数不超过m的划分数 7 | (2)正整数n划分为不超过m个正整数之和的划分数 8 | (3)正整数n划分为若干正奇整数之和的划分数 9 | (4)正整数n划分为互不相同正整数之和的划分数 10 | 约定: 11 | 整数划分无顺序,比如对7划分,认为2 2 3和3 2 2和2 3 2为同一种划分。 12 | 13 | 14 | ###输入格式: 15 | 两个数n和m,中间空格相连。n和m都不超过100。 16 | 17 | 如输入7 3 则: 18 | - 最大加数不超过3的划分为:(3 3 1)(3 2 2)(3 2 1 1)(3 1 1 1 1)(2 2 2 1)(2 2 1 1 1) 19 | (2 1 1 1 1 1)(1 1 1 1 1 1 1),共8种。 20 | - 不超过3个正整数的划分为:(7)(6 1)(5 2)(5 1 1)(4 3)(4 2 1)(3 3 1)(3 2 2),共8种。 21 | - 若干正奇数的划分为:(7)(5 1 1)(3 3 1)(3 1 1 1 1)(1 1 1 1 1 1 1),共5种。 22 | - 互不相同正整数的划分为:(7)(6 1)(5 2)(4 3)(4 2 1),共5种。 23 | 24 | 25 | ### 输出格式: 26 | 四个数,中间空格相连,分别为上面四个题例的结果。其中m参数只和题例(1)和(2)有关, 27 | 与(3)(4)无关。 28 | 29 | 30 | ### 输入样例: 31 | 7 3 32 | 33 | 34 | ### 输出样例: 35 | 8 8 5 5 36 | 37 | --- 38 | 39 | [解答](../源码/2-8.cpp) -------------------------------------------------------------------------------- /题目/2-9 矩阵连乘积的加括号方式数.md: -------------------------------------------------------------------------------- 1 | #2-9 矩阵连乘积的加括号方式数 2 | 3 | ###Description: 4 | 给定n个矩阵{A1,…,An},其中Ai和Ai+1可乘,i=1,2,…,n-1。考察矩阵连乘积加括弧的方式数。 5 | 6 | 如四个矩阵连乘积A1A2A3A4,共有五种不同的加括弧方式: 7 | ( A1 ( A2 ( A3 A4 ) ) ) 8 | ( A1 ( ( A2 A3 ) A4 ) ) 9 | ( ( A1A2 ) ( A3 A4 ) ) 10 | ( A1 ( A2 A3 ) A4 ) 11 | ( ( ( A1 A2 ) A3 ) A4 ) 12 | 13 | ###输入示例: 14 | 4 15 | ###输出示例: 16 | 5 17 | 18 | 19 | 20 | ###输入格式: 21 | 输入矩阵连乘积的个数n(n<=20)。 22 | 23 | 24 | ###输出格式: 25 | 输出矩阵连乘积加括号的方式数。 26 | 27 | 28 | ###输入样例: 29 | 4 30 | 31 | 32 | ###输出样例: 33 | 5 34 | 35 | --- 36 | 37 | [解答](../源码/2-9.cpp) 38 | 39 | -------------------------------------------------------------------------------- /题目/3-1 最大长方体问题.md: -------------------------------------------------------------------------------- 1 | #3-1 最大长方体问题 2 | --- 3 | 4 | ####Description 5 | >一个长,宽,高分别是m,n,p的长方体被分割成m*n*p个小立方体。每个小立方体内含一个整数。 6 | 试着设计一个算法,计算所给长方体的最大子长方体。子长方体的大小由它内部所含所有整数之和确定。 7 | 8 | *约定:当该长方体所有元素均为负数时,输出最大子长方体为0。 * 9 | 10 | 11 | ####输入格式 12 | 第一行3个正整数m,n,p,其中 1<=m,n,p<=50 13 | 接下来的m*n行中每行p个整数,表示小立方体中的数。 14 | 15 | 16 | ####输出格式 17 | 第一行中的数是计算出的最大子长方体的大小。 18 | 19 | 20 | ####输入样例 21 | 3 3 3 22 | 0 -1 2 23 | 1 2 2 24 | 1 1 -2 25 | -2 -1 -1 26 | -3 3 -2 27 | -2 -3 1 28 | -2 3 3 29 | 0 1 3 30 | 2 1 -3 31 | 32 | ####输出样例 33 | 14 34 | 35 | 36 | 37 | ----------- 38 | 39 | 40 | [解答](../源码/3-1.cpp) 41 | -------------------------------------------------------------------------------- /题目/3-2 最长上升子序列.md: -------------------------------------------------------------------------------- 1 | # 3-2最长上升子序列 2 | --- 3 | 4 | 5 | ###Description: 6 | 7 | A numeric sequence of ai is ordered if a1 < a2 < ... < aN. 8 | Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK), 9 | where 1 <= i1 < i2 < ... < iK <= N. 10 | For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. 11 | All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8). 12 | 13 | Your program, when given the numeric sequence, must find the length of its longest ordered subsequence. 14 | 15 | 16 | 17 | ###输入格式: 18 | There are several test cases. Every test case includes two lines. 19 | The first line contains the length of sequence N. The second line contains the elements of sequence - N integers 20 | in the range from 0 to 10000 each, 21 | separated by spaces. 1 <= N <= 1000 22 | When N is 0, it indicates test to end. 23 | 24 | 25 | ###输出格式: 26 | Output must contain a single integer for every test case ---- the length of the longest ordered subsequence 27 | of the given sequence. 28 | 29 | 30 | ###输入样例: 31 | 32 | 7 33 | 34 | 1 7 3 5 9 4 8 35 | 36 | 6 37 | 38 | 1 8 3 6 5 9 39 | 40 | 5 41 | 42 | 1 2 3 4 5 43 | 44 | 0 45 | 46 | 47 | ###输出样例: 48 | 4 49 | 4 50 | 5 51 | 52 | 53 | 54 | [解答](../源码/3-2.cpp) -------------------------------------------------------------------------------- /题目/3-3 最长公共子字符串.md: -------------------------------------------------------------------------------- 1 | # 3-3 最长公共子字符串 2 | --- 3 | 4 | ###Description: 5 | 求两个输入序列的最长的公共子字符串的长度。子字符串中的所有字符在源字符串中必须相邻。 6 | 7 | 如字符串:21232523311324和字符串312123223445,他们的最长公共子字符串为21232,长度为5。 8 | 9 | 10 | 11 | ###输入格式: 12 | 两行,第一行为第一个字符串X,第二行为第二个字符串Y,字符串不含空格并以回车标示结束。X和Y的串长都 13 | 不超过100000。 14 | 15 | 16 | 输出格式: 17 | 两行,第一行为最长的公共子字符串的长度,第二行输出一个最长的公共子字符串。 18 | 19 | 说明: 20 | (1)若最长的公共子字符串有多个,请输出在源字符串X中靠左的那个。 21 | (2)若最长公共子字符串的长度为0,请输出空串(一个回车符)。 22 | 23 | 如输入: 24 | 25 | 21232523311324 26 | 27 | 152341231 28 | 29 | 由于523和123都是最长的公共子字符串,但123在源串X中更靠左,因此输出: 30 | 3 31 | 32 | 123 33 | 34 | 35 | ###输入样例: 36 | 21232523311324 37 | 38 | 312123223445 39 | 40 | 41 | ###输出样例: 42 | 5 43 | 44 | 21232 45 | 46 | --- 47 | 48 | [解答](../源码/3-3.cpp) -------------------------------------------------------------------------------- /题目/3-4 数字三角.md: -------------------------------------------------------------------------------- 1 | # 3-4 数字三角 2 | --- 3 | 4 | ### Description 5 | 问题描述:给定一个由n行数字组成的数字三角形,如下图所示。试用动态规划算法,计算出从三角 6 | 顶部至底部的一条路径,使得该路径经过的数字总和最大。 7 | 8 | 注意每个数字只能走向下一行左边或右边的数字,而不能跳跃的走。 9 | 10 | ``` 11 | 7 12 | 13 | 3 8 14 | 15 | 8 1 0 16 | 17 | 2 7 4 4 18 | 19 | 4 5 2 6 5 20 | 21 | ``` 22 | 23 | 24 | ### 输入格式 25 | 第一行是数字三角的行数n,1<=n<=100。接下来n行是数字三角各行中的数字,所有数字在0~99之间。 26 | 27 | 28 | ### 输出格式 29 | 输出两行,第一行是计算出的最大路径的和值,第二行是该路径上的数字。若有多条路径,靠右的路径 30 | 优先(即仅仅输出靠右的路径即可,无需多条路径都输出)。 31 | 32 | 33 | 如: 34 | Input: 35 | 36 | 5 37 | 38 | 7 39 | 40 | 3 8 41 | 42 | 8 1 6 43 | 44 | 2 7 4 4 45 | 46 | 4 5 2 4 5 47 | 48 | 有两条路径:7-3-8-7-5和7-8-6-4-5都为30,由于后者靠右,因此仅输出后者。 49 | Output: 50 | 30 51 | 7 8 6 4 5 52 | 53 | 54 | ### 输入样例 55 | 56 | 5 57 | 58 | 7 59 | 60 | 3 8 61 | 62 | 8 1 0 63 | 64 | 2 7 4 4 65 | 66 | 4 5 2 6 5 67 | 68 | 69 | 70 | ### 输出样例 71 | 30 72 | 73 | 7 3 8 7 5 74 | 75 | --- 76 | [解答](../源码/3-4.cpp) 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /题目/3-5 最大m段乘积和最小m段和.md: -------------------------------------------------------------------------------- 1 | # 3-5 最大m段乘积和最小m段和 2 | 3 | ### Description 4 | 一个n位十进制整数S,若将S划分为m个段,则可以得到m个整数。 5 | (1)这m个整数的乘积称为S的一个“m段乘积”,对于给定的S和m,求S的最大m段乘积。 6 | (2)这m个整数的和称为S的一个“m段和”,对于给定的S和m,求S的最小m段和。 7 | 8 | 9 | 10 | ### 输入格式 11 | 输入:三个整数,n,m,S。 12 | 第一个n表示S的位数,第二个m表示分割的段数,第三个数为需要被分段的n位十进制数S。 13 | n、m和S三个数中间空格相连,这里1<=m<=n,n<=10,即S、S的最大m段乘积、S的最小m段和这三个 14 | 数都用int型即可,虽然输出的数可能很大,但这里32位整数够了,测试数据没有超过32位整数,即 15 | 无需考虑多位的高精度数。 16 | 17 | 例如,十进制数3456的“最大3段乘积”为1020。因为3456划分3个段有如下情形: 18 | 3*4*56=672,3*45*6=810,34*5*6=1020。 19 | 3456的“最小3段和”为45,因为3+4+56=63,3+45+6=54,34+5+6=45。 20 | 21 | 22 | ### 输出格式 23 | 输出: 24 | 第一行计算出的最大m段乘积和最小m段和,中间空格相连。 25 | 第二行写出最大m段乘积的乘法表达式。 26 | 第三行写出最小m段和的加法表达式。 27 | 28 | 这里约定: 29 | (1)若有多种分段方法使得最大m段乘积相等且都最大,则优先输出靠左的段更短小的这种方式。 30 | 比如输入3 2 111,这里最大m段乘积的乘法表达式为:1*11=11,而不要输出11*1=11 31 | (2)若分出的某段数字有0开头的,不输出0。 32 | 比如输入5 3 20001,这里输出:2*0*1=0,(其实代表着:2*0*001,这里001,只写1即可) 33 | (3)若只分一个段,表达式也就写1个段的数等于某个数。 34 | 比如输入2 1 12,输出:12=12 35 | (4)最小m段和的加法表达式也同样满足前面的约定(1)(2)(3)。 36 | 37 | 38 | ### 输入样例 39 | 4 3 3456 40 | 41 | 42 | ### 输出样例 43 | 1020 45 44 | 34*5*6=1020 45 | 34+5+6=45 46 | 47 | [解答](../源码/3-5.cpp) -------------------------------------------------------------------------------- /题目/3-6 不能移动的石子合并.md: -------------------------------------------------------------------------------- 1 | # 3-6 不能移动的石子合并(考) 2 | 3 | ### Description 4 | 做如下两个模型的石子合并,如下模型石子都不能移动出列,且合并都仅发生在相邻两堆石子中: 5 | 6 | (1)第一个模型:一行排列且相邻合并 7 | 有n堆石子A1,A2,...,An形成一行,每堆石头个数记为ai(1<=i<=n),相邻两堆可合并,合并的分值为新堆的 8 | 石子数。求合并为一堆的最低得分和最高得分。 9 | 10 | (2)第二个模型:一圈排列且相邻合并 11 | 有n堆石子A1,A2,...,An形成首位相连的一个环形,An和A1相邻,每堆石头个数记为ai(1<=i<=n),相邻两堆 12 | 可合并,合并的分值为新堆的石子数。求合并为一堆的最低得分和最高得分。 13 | 14 | 例如4堆石子,每堆石子个数:9 4 4 5 15 | 若排成一行,最小分值:(4+4)+(8+5)+(9+13)=43,最大分值:(9+4)+(13+4)+(17+5)=52。 16 | 若排成圈状,最小分值:(4+4)+(8+5)+(9+13)=43,最大分值:(9+5)+(14+4)+(18+4)=54。 17 | 18 | 此题以第一模型的最低得分为例,很多同学想着采用总是从最小的相邻两堆下手的思想,认为最后获得的也就是最 19 | 低得分。但这个贪心策略是不对的。如下反例: 20 | 21 | 石子:9 4 6 1 5 22 | 23 | 贪心策略: 24 | 9 4 6 6 计分6 25 | 9 10 6 计分10 26 | 9 16 计分16 27 | 25 计分25 28 | 得分共计:6+10+16+25=57 29 | 30 | 但9 4 6 1 5 若如下方式合并: 31 | 13 6 1 5 计分13 32 | 13 6 6 计分6 33 | 13 12 计分12 34 | 25 计分25 35 | 13+6+12+25=56 36 | 37 | 或 38 | 9 4 6 6 计分6 39 | 9 4 12 计分12 40 | 13 12 计分13 41 | 25 计分25 42 | 6+12+13+25=56 43 | 44 | 后两种方式合并出的56都比贪心策略的57来的更低,因为总选择最小的相邻两堆去合并,并不能保证后续每步 45 | 都可以最小,也许这轮最小导致后续几轮分值较大。 46 | 47 | 48 | 49 | ### 输入格式 50 | 两行。第一行n,第二行a1 a2 … an,每个ai(1<=i<=n)表示第i堆石子的个数,n<=100 51 | 52 | 53 | ### 输出格式 54 | 两行。第一行是第一个模型的最低得分和最高得分,中间空格相连,第二行是第二个模型的最低得分和 55 | 最高得分,中间空格相连。 56 | 57 | 58 | ### 输入样例 59 | 4 60 | 9 4 4 5 61 | 62 | 63 | ### 输出样例 64 | 43 52 65 | 43 54 66 | 67 | --- 68 | 69 | [解答](../源码/3-6.cpp) -------------------------------------------------------------------------------- /题目/4-1 区间相交问题.md: -------------------------------------------------------------------------------- 1 | # 区间相交问题 2 | --- 3 | 4 | ### Description 5 | 给定x轴上n个闭区间,去掉尽可能少的闭区间,使剩下的闭区间都不相交。 6 | 注意:这里,若区间与另一区间之间仅有端点是相同的,不算做区间相交。 例如,[1,2]和[2,3]算是不相交区间。 7 | 8 | 9 | ### 输入格式 10 | 第一行一个正整数n(n<=50),表示闭区间数。 11 | 接下来n行中,每行2个整数,表示闭区间的2个整数端点。 12 | 13 | 14 | ### 输出格式 15 | 输出去掉的最少的闭区间数。 16 | 17 | 18 | ### 输入样例 19 | 3 20 | 21 | 10 20 22 | 23 | 10 15 24 | 25 | 12 15 26 | 27 | ### 输出样例 2 28 | 29 | [解答](../源码/4-1.cpp) -------------------------------------------------------------------------------- /题目/4-2 可以移动的石子合并.md: -------------------------------------------------------------------------------- 1 | # 4-2 可以移动的石子合并(考) 2 | ### Description 3 | 有n堆石子形成一行(a1,a2,…,an,ai为第i堆石子个数),现要将石子合并成一堆,规定每次可 4 | 选择至少2堆最多k堆移出然后合并,每次合并的分值为新堆的石子数。 5 | 6 | 若干次合并后,石子最后肯定被合并为一堆,得分为每次合并的分值之和。 7 | 8 | 现在求解将这n堆石子合并成一堆的最低得分和最高得分。 9 | 10 | 11 | 12 | ### 输入格式 13 | 两行。第一行n和k。 14 | 第二行a1 a2 … an,每个ai(1<=i<=n)表示第i堆石子的个数,n<=200,2<=k<=n。 15 | 16 | 17 | ### 输出格式 18 | 仅一行,为石子合并的最低得分和最高得分,中间空格相连。 19 | 20 | 21 | ### 输入样例 22 | 7 3 23 | 45 13 12 16 9 5 22 24 | 25 | 26 | ### 输出样例 27 | 199 593 28 | 29 | [解答](../源码/4-2.cpp) 30 | -------------------------------------------------------------------------------- /题目/5-1 骑士问题.md: -------------------------------------------------------------------------------- 1 | # 5-1 骑士问题(考) 2 | 3 | 4 | 题型: 编程题 语言: G++;GCC;VC;JAVA 5 | Description 6 | 在一个标准8×8的国际象棋棋盘上,棋盘中有些格子是可能有障碍物的。已知骑士的初始 7 | 位置和目标位置,你的任务是计算出骑士最少需要多少步可以从初始位置到达目标位置。 8 | 有障碍物的格子当然不可能到达。 9 | 10 | 标准的8×8的国际象棋棋盘中每一个格子可以用唯一的编号确定。行用1~8这8个数字依次表示, 11 | 列用“a”~“h”这8个字母依次表示。例如下图(a)的骑士所在位置(图中有n的格子)的编 12 | 号为“d4”(注意“d”和“4”之间没有空格)。 13 | 14 | 15 | 16 | 我们知道国际象棋中的骑士可以按“L”路线移动(一个方向走2个格子,接着垂直方向走一个格子)。 17 | 因此,如图(a)所示的骑士(位于d4),可以到达位置c2,b3,b5,c6,e6,f5,f3和e2(图中 18 | 有“x”标记的格子)。此外,骑士不能移出棋盘。 19 | 20 | 骑士可以按照移动规则自由地在棋盘上没有障碍物的格子中移动。图(b)给出了一个骑士移动的例 21 | 子,也就是输入样例中第一组数据对应的例子。初始格子用“n”标记,目标格子用“N”标记,有 22 | 障碍物的格子用“b”标记。一个可行的移动序列在图中用数字标记出来(a1,b3,a5,c6,e5,g4, 23 | h2,f1)。 总共需要7步才能完成。事实上,这也是最少的步数了。 24 | 25 | 26 | 27 | ### 输入格式 28 | 输入包含1个或多个测试数据。 29 | 每一个测试数据的第一行是一个整数b(-1 <= b <= 62),表示棋盘中有障碍物的格子数目。 30 | 当b=-1时,输入结束。 31 | 第二行含b个不同的障碍物的格子编号,用空格隔开。当b=0时,此行为空行。 32 | 第三行是骑士的初始格子和目标格子的编号,也是用空格隔开。初始格子和目标格子是不同的,且 33 | 都没有障碍物。 34 | 35 | 36 | ### 输出格式 37 | 对于每个数据,输出一行。格式: 38 | Board i:m moves 39 | 其中i表示数据的组序号(从1开始),m表示骑士所用的最小步数。 40 | 如果骑士无法到达目标格子,输出: 41 | Board i:not reachable 42 | 43 | 44 | ### 输入样例 45 | 10 46 | c1 d1 d5 c2 c3 c4 d2 d3 d4 c5 47 | a1 f1 48 | 0 49 | 50 | c1 b3 51 | 2 52 | b3 c2 53 | a1 b2 54 | -1 55 | 56 | 57 | ### 输出样例 58 | Board 1:7 moves 59 | Board 2:1 moves 60 | Board 3:not reachable 61 | 62 | -------------------------------------------------------------------------------- /题目/5-2 子集和问题.md: -------------------------------------------------------------------------------- 1 | # 5-2 子集和问题(考) 2 | 3 | 4 | 题型: 编程题 语言: G++;GCC;VC;JAVA 5 | Description S是一个整数集合,S={x1,x2,...,xn},c是一个整数。这里集合元素xi(1<=i<=n)和c都是整数,可能为负。 6 | 7 | 子集和问题就是:判断是否存在S的一个子集S1,使得: 8 | $$\sum_{x \in S1} X=C$$ 9 | 10 | 对S集合子集树采用深度优先的顺序进行搜索,子集树从上到下每层标示着S集合中每个从左到右元素“选”或者“不选”(左1右0)。 11 | 12 | 试着用回溯算法设计解子集和问题。 13 | 14 | 15 | 16 | 17 | ### 输入格式 18 | 第一行2个数:正整数n和整数c。n表示S集合的大小(n<=100),c是子集和的目标值。 19 | 接下来一行中,有n个整数,表示集合S中的元素。 20 | 21 | 22 | ### 输出格式 23 | 将子集和问题的解输出,当无解时,输出"No Solution"(注意No Solution的大小写,空格,无标点)。 24 | 注意:依据S集合元素从左到右依次来画子集树,因此子集树唯一。 25 | 若存在多种子集和问题的解时,只输出在这个唯一的子集树按深度优先方向遇到的第一个解,这样保证解的唯一性,利于评判。 26 | 如:5 10 27 | 2 2 6 3 3 28 | 这里,2+2+6=10,2+2+3+3=10,但只输出2 2 6 29 | 30 | 如:5 10 31 | 2 2 3 3 6 32 | 只输出2 2 3 3 33 | 34 | 又如:5 -30 35 | 2 -2 6 -30 -3 36 | 只输出2 -2 -30 37 | 38 | 39 | ### 输入样例 40 | 5 10 41 | 2 2 6 5 4 42 | 43 | 44 | ### 输出样例 45 | 2 2 6 46 | 47 | -------------------------------------------------------------------------------- /题目/5-3工作分配问题.md: -------------------------------------------------------------------------------- 1 | #5-3工作分配问题(必做) 2 | 3 | ### Description 4 | 有n件工作分配给n个人,将工作i分配给第j个人需要支付劳务费用Cij。请为每人分配一个工作,并使得总劳务费用达到最小。 5 | 6 | 7 | 8 | ### 输入格式 9 | 第一行一个正整数n(1<=n<=11),表示n个工作数,接下来n行,每行代表第i个工作支付给n个不同的人的劳务费用。 10 | 11 | 12 | ### 输出格式 13 | 两行。 14 | 第一行为最小的总劳务费用。 15 | 第二行有n个数,表示工作分配方案。 16 | 如下面sample用例的测试数据: 17 | 第二行是2 1 3,表示第1个工作分配给第2个人,第2个工作分配给第1个人,第3个工作分配给第3个人。 2+2+5=9 18 | 当同时有多种分配方案都能使得总劳务费用相同且都最小,小编号工作优先分配给小编号的人,仅输出这一种方案即可。 19 | 20 | 21 | ### 输入样例 22 | 3 23 | 10 2 3 24 | 2 3 4 25 | 3 4 5 26 | 27 | 28 | ### 输出样例 29 | 9 30 | 2 1 3 31 | 32 | [解答](../源码/5-3.cpp) 33 | -------------------------------------------------------------------------------- /题目/5-4 多机最佳调度.md: -------------------------------------------------------------------------------- 1 | # 5-4 多机最佳调度(考) 2 | 3 | ### Description 4 | 假设有n个任务(n<=100),m台机器(m<=50),任务可以由任何一个机器完成,完成任务i需要的时间为ti, 5 | 请设计两种算法(一种采用贪心算法,另一种采用回溯算法),找出完成这n个任务的最佳调度,使得最早时间完成全部任务。 6 | 7 | 这里采用两种算法来求解: 8 | 1)贪心算法可以得到近似的最早完成时间,算法思想在书上4.7节。 9 | 2)回溯算法搜索m叉树(除叶节点外每个节点m个儿子),寻找最早的完成时间。 10 | 11 | 12 | 13 | ### 输入格式 14 | 输入两行,第一行为n和m,中间空格相连(其中n表示任务的数量,m表示机器的数量),(n<=100, m<=50)。 15 | 第二行的n个数是任务i的处理时间ti。 16 | 17 | 18 | ### 输出格式 19 | 输出两行,第一行为采用贪心算法算出的最早完成时间,第二行为采用回溯算法搜索出的最早完成时间。 20 | 21 | 22 | ### 输入样例 23 | 7 3 24 | 2 14 4 16 6 5 3 25 | 26 | 另一个输入示例: 27 | 14 3 28 | 10 10 10 10 10 7 7 7 7 7 5 5 5 5 29 | 30 | 31 | ### 输出样例 32 | 17 33 | 17 34 | 35 | 另一个输出示例: 36 | 37 37 | 35 38 | 39 | [解答](../源码/5-4.cpp) 40 | -------------------------------------------------------------------------------- /题目/提示.md: -------------------------------------------------------------------------------- 1 | # 提示 2 | --- 3 | [toc] 4 | 5 | ## 2-1有重复元素的排列问题 6 | 课本上有“递归”实现无重复元素全排列的源程序。 7 | 稍加修改即可满足此题要求。 8 | 9 | 在递归产生全排列的那段程序开始之前, 10 | 加一个判断:判断第i个元素是否在list[k,i-1]中出现过。 11 | ``` 12 | PermExcludeSame(char list[], int k, int m) 13 | { 14 | ...... 15 | for (int i=k; i<=m; i++) 16 | { 17 | if (Findsame(list,k,i))//判断第i个元素是否在list[k,i-1]中出现过 18 | continue; 19 | Swap (list[k], list[i]); 20 | PermExcludeSame(list, k+1, m); 21 | Swap (list[k], list[i]); 22 | } 23 | } 24 | ``` 25 | 26 | ## 2-4整数因子分解 27 | 此题因子讲顺序的.第一个因子可能是2~n之间的数. 28 | 比如对12而言,第一个因子可能是2,3,4,6,12. 29 | 30 | 将第一个因子为2的分解个数,加上第一个因子为3的分解个数,...,直至加到第一个因子为12的分解个数. 31 | 32 | 而第一个因子为2的分解个数又是多少呢?是6(因为12/2=6)的分解个数,递归求解! 33 | 34 | 可用“递归”和“备忘录方法”两种方法分别求解,并测试一下效率。 35 | 36 | 递归实现整数因子分解的计数。假设对正整数n的因子分解计数为solve(n)。 37 | 1)当n=1时,计数加1。 38 | 2)当n>1时,对n的每个因子i,计算solve(n/i)。 39 | 40 | 或者这样实现也可以: 41 | ``` 42 | int solve2(int n) 43 | { 44 | int num=0; 45 | 46 | if(n==1) return 1; 47 | 48 | for(int i=2; i<=n; i++) 49 | if(n%i == 0) num+=solve2(n/i); 50 | 51 | return num; 52 | } 53 | 54 | ``` 55 | ## 2-5两个有序数序列中找第k小 56 | 假设:X序列为X[xBeg...xEnd],而Y序列为Y[yBeg...yEnd]。 57 | 58 | 将序列X和Y都均分2段,即取X序列中间位置为 xMid (xMid = xBeg+(xEnd-xBeg)/2),也同理取序列Y中间位置为yMid。 59 | 比较X[xMid]和Y[yMid]的大小,此时记录X左段和Y左段元素个数合计为halfLen,即halfLen = xMid-xBeg+yMid-yBeg+2。 60 | 61 | 1. 当X[xMid] < Y[yMid]时,在合并的数组中,原X[xBeg...xMid]所有元素一定在Y[yMid]的左侧, 62 | (1) 若k < halfLen,则此时第k大的元素一定不会大于Y[yMid]这个元素, 63 | 故以后没有必要搜索 Y[yMid...yEnd]这些元素,可弃Y后半段数据。 64 | 此时只需递归的对X序列+Y序列的前半段,去搜索第k小的数。 65 | 66 | (2) 若k >= halfLen,则此时第k大的元素一定不会小于X[xMid]这个元素, 67 | 故以后没有必要搜索 X[xBeg...xMid]这些元素,可弃X前半段数据。 68 | 此时只需递归的对X序列的后半段+Y序列,去搜索第 k-(xMid-xBeg+1)小的数。 69 | 70 | 2. 当X[xMid] >= Y[yMid]时,在合并的数组中,原Y[yBeg...yMid]的所有元素一定在X[xMid]的左侧, 71 | (1) 若k < halfLen,则此时第k大的元素一定不会大于X[xMid]这个元素, 72 | 故以后没有必要搜索 X[xMid...xEnd]这些元素,可弃X后半段数据。 73 | 此时只需递归的对X序列的前半段+Y序列,去搜索第k小的数。 74 | 75 | (2) 若k >= halfLen,则此时第k大的元素一定不会小于Y[yMid]这个元素, 76 | 故以后没有必要搜索 Y[yBeg...yMid]这些元素,可弃Y前半段数据。 77 | 此时只需递归的对X序列+Y序列的后半段,去搜索第 k-(yMid-yBeg+1)小的数。 78 | 79 | 递归的边界,如何来写? 80 | 1) if (xBeg > xEnd) return Y[yBeg + k - 1]; //X序列为空时,直接返回Y序列的第k小元素。 81 | 2) if (yBeg > yEnd) return X[xBeg + k - 1]; //Y序列为空时,直接返回X序列的第k小元素。 82 | 83 | 84 | 效率分析: 85 | 86 | T(m,n)表示对长度为m的有序的X序列和长度为n的有序的Y序列,搜索第k小元素的复杂度。 87 | T(m,n)=1 m=0或n=0 88 | T(m,n) <= max{T(m/2,n), T(m,n/2)} + O(1) 89 | 90 | 则T(m,n) = O(max{logm, logn}) 91 | 92 | 93 | ## 2-8整数划分的扩展问题 94 | 这四个问题,不但要分析递归关系,还要正确写出递归的边界,有时边界值错一个,结果都不一定对,因此要小心。 95 | 96 | 1, 问题(1)即为书上例2-5。 97 | 98 | 99 | 100 | 2, 问题(2)也可以这样来想: 101 | 设d[i][j]表示i划分为j份,视为i个球放入j个盒子的方法数。d[n][m]为题目问题(2)所求,d[i][j]有如下递推关系: 102 | (1)j个盒子有空的,d[i][j]=d[i][j-1],把某一空盒子拿出来放一边 103 | (2)j个盒子都不空,d[i][j]=d[i-j][j],每个盒子扣掉1个球 104 | 因此,d[i][j] = d[i][j-1] + d[i-j][j], j>1; 105 | 特别的有: d[k][0]=0, k>=1; d[k][1]=1, k>=1 106 | 107 | 108 | 109 | 3, 问题(1)和问题(2)是结果是相同的。 110 | 可以证明,任何一个问题(1)的解都可以转化为另一个问题(2)的解,一一对应,因此记数相同。 111 | 112 | 这个证明思路可以参考如下: 113 | 对正整数n进行划分,不超过m个的划分,视为n块立方体积木堆成m列,垂直的来看n=n1+n2+...+nm。 114 | 对每一个堆法,将视角旋转90度,即水平方向上看,其实都对应一个最大加数不超过m的划分。 115 | 因此,对正整数n进行划分,不超过m个的划分,换种视角,都对应于一个最大加数不超过m的划分。反之亦然。 116 | 所以,这种对应是一一对应。因此,不超过m个加数的划分数 = 最大加数不超过m的划分数。 117 | 118 | 如下图所示: 119 | ![Alt text](./1449550169950.png) 120 | 121 | 122 | 123 | 124 | 4, 设: Odd(I,J)表示I划分为J个正奇数的划分数; Even(I,J)表示I划分为J个正偶数的划分数. 125 | 有如下递推关系: 126 | (1)Even(I,J) = Odd(I-J,J), 每个偶加数减掉1变为奇加数 127 | (2)Odd(I,J) = Odd(I-1,J-1) + Even(I-J,J), 加数含1的方式数 + 加数不含1的方式数 128 | (3)特别的: J>I || J<0, Odd(I,J)=0; 129 | Odd(1,1)=1, Odd(2,1)=0, Odd(2,2)=1 130 | J>I || J<0, Even(I,J)=0; 131 | Even(1,1)=0, Even(2,1)=1, Even(2,2)=0 132 | 133 | 134 | 135 | 5, 思想: 转化为子集和问题: 集合{1,2,…,n},挑选若干正整数,使之和为n,这也是一个背包装物品问题。 136 | 设F(I,J): 表示挑选集合前I个,使之和为J的方式数。 137 | 递推关系: F(I,J) = F(I-1,J) + F(I-1,J-I), 第I个不挑的方式数 + 挑第I个的方式数 138 | 递推边界: F(1,1)=1, F(1,k)=0(k>1), F(I,k)=0(k<0), F(I,0)=1(I>0) 139 | (这里一个都没挑,值为0,也是一种方式,所以为1) 140 | 141 | 142 | 143 | 6, 其实这第(4)个问题的解也和第(3)问题的解是相同的 144 | 145 | 146 | ## 3-2最长上升子序列 147 | 一,对输入字符串的处理 148 | 149 | 注意:这道题和其他题的输入输出不同,这题是接收多组数据而非单组,以0来判别结束。 150 | 大家在接受数据的时候,不要用(c=getchar())!='\n'诸如此类一个字符一个字符接受, 151 | 然后判断是否是回车符号来结束一行的输入,这样的方式在你本机运行不会有问题, 152 | 但OJ系统中会有错误,无法输出结果,因为测试平台行末并非'\n'字符。 153 | 这里接受数据用scanf的%d或%s,或cin等,会自动判别结束字符的,你就不要在你程序 154 | 里专门去判别或吸收回车字符。 155 | 156 | 对于最后一组数据输入为0表示结束,只要判断接受的第一个字符是否为0且字 157 | 符串长度为1就结束,无须去处理回车字符。 158 | 159 | 输入的序列可以用整型数组或字符串数组保存。 160 | 161 | 162 | 二,算法的动态规划思想 163 | 164 | 考虑采用动态规划算法,针对每个元素,以该元素结尾的最长有序子序列作为子问题, 165 | 计算出每个子问题的最大长度用“表”记录下来。先写出递推关系式再编程实现。 166 | 167 | 设f(i)表示:从左向右扫描过来直到以a[i]元素结尾的序列,可获得的最长上升 168 | 子序列的长度,且最长上升子序列包含a[i]元素(1<=i<=n)。 169 | 170 | (这里大家思考一下,为何要这样假设子问题和子问题最优解f(i)? 171 | 有同学问:为何最长上升子序列要包含a[i]元素(1<=i<=n)? 172 | 因为你所设的子问题要和更下一级子问题关联起来。如果长度为i序列的最长上升 173 | 子序列中没有规定包含a[i]元素,那如何和其前缀的最长上升子序列问题关联起来 174 | 呢,那样显然是比较麻烦的。) 175 | 176 | f(i)是从f(1),f(2), ……到f(i-1)中找最大的一个值,再加1,或者就是1。 177 | 这主要得看a[i]这个元素能否加入到之前已经获得的最长上升子序列当中去, 178 | 如果能加入,是之前已获得的最长上升子序列长度加1; 179 | 如果不能加入,就开始一个新的上升子序列,长度为1。 180 | 最后,所要求的整个序列的最长上升子序列长度为 max{ f(i): 1<=i<=n } 181 | 182 | f(i)的递推公式如下: 183 | (1)f(i) = 1 当i=1; 184 | (2)f(i) = max{f(j)+1} 当i>1, 对某个前面的j(1<=j1, 对任意j(1<=j=a[i] 186 | 187 | 例子,对于序列:4 2 6 3 1 5 2 188 | i = 1 2 3 4 5 6 7 189 | a[i] = 4 2 6 3 1 5 2 190 | f(i) = 1 1 2 2 1 3 2 191 | 192 | 这里max{f(i)}=3为原问题所求的最长上升子序列的长度。 193 | 194 | 效率分析: 195 | f(i)的计算不超过O(n),因此,整个算法为O(n^2)。 196 | 197 | 198 | ## 3-3最长公共子字符串 199 | 一,对输入字符串的处理 200 | 大家在接受数据的时候,不要用(c=getchar())!='\n'诸如此类一个字符一个字符接受,然后判断是否是回车 201 | 符号来结束一行的输入,这样的方式在你本机运行不会有问题,但OJ系统中会有错误,无法输出结果,因为 202 | 测试平台行末并非'\n'字符。这里接受数据用scanf的%s,或cin等,会自动判别结束字符的,你就不要在你 203 | 程序里专门去判别或吸收回车字符。 204 | 205 | 二,递推公式 206 | 此题和书上3.3节"最长公共子序列"描述是不同的. 207 | 子序列不连续,子字符串认为是连续的. 208 | 此题更加简单! 209 | 210 | 假设求字符串str1,str2的最长公共子串的长度. 211 | 定义f(m,n): 分别以str1[m],str2[n]结尾的最长连续公共子串的长度, 212 | 其中字符串末尾的str1[m]和str2[n]包含在最长公共子串中,即为最长公共子串的最末元素。 213 | 214 | (这里大家思考一下,为何要这样假设子问题和子问题最优解f(m,n)? 215 | 因为子串是连续的,更大规模问题和下一级更小规模的子问题要能联系起来,而且这种联系还要越简单越好, 216 | 只有规定原先两个串的最末元素包含在最长公共子串中,这样就能联系上两个串的前缀部分(都去掉末个元 217 | 素)的最长公共子串问题。) 218 | 219 | 而对于f(m+1,n+1) 有: 220 | 1) f(m+1,n+1) = 0, if str1[m+1] != str2[n+1] 221 | 2) f(m+1,n+1) = f(m,n) + 1, if str1[m+1] == str2[n+1] 222 | 3) 另外边界情况,f(0,j)=0(j>=0), f(j,0)=0(j>=0) 223 | 224 | 而此题所求的最长公共字符串的长度即为f(m,n)整个二维数组中的最大值,注意不是填充的最后一个元素。 225 | 至于如何优先输出在源串X靠左的公共子串,大家自行思考。 226 | 227 | ## 3-6不能移动的石子合并 228 | 229 | (1)第一个石子合并模型 230 | 231 | 和书上3.1节的矩阵连乘问题类似。假设m[i,j]为合并石子ai…aj,1<=i<=j<=n。所得到的最小 232 | 得分,若没有“合并”这个动作,则为0。 233 | 原问题所求的合并最小值即为m[1,n]。 234 | 235 | 递推公式如下,其中min表示求最小,sum表示求和。 236 | m[i,j] = 0, if i=j 237 | m[i,j] = min{ m[i,k]+m[k+1,j] | for all k, i<=kn),看首次调用backtrack参数是0还是1 385 | { 386 | …… //这个省略号,要你自己来扩展了,要找到最好的叶子(即最早完成时间的一组最优调度) 387 | return; 388 | } 389 | 390 | for(int i = 0; i < m; i++) 391 | { 392 | len2[i] += t[dep]; //len2数组和t数组如上说明 393 | x[dep] = i+1; 394 | 395 | if(len2[i] < best) 396 | { 397 | backtrack(dep+1); 398 | } 399 | 400 | len2[i] -= t[dep]; 401 | } 402 | } 403 | ``` 404 | 405 | --------------------------------------------------------------------------------