1 / 24

程序设计实习

程序设计实习. 习题讲解 李超 lichaopku@gmail.com. 最长上升子序列 (ai2757). 问题描述: 给出一个由 n 个数组成的序列 a[1..n] ,找出它的最长单调上升子序列。即求最大的 m, 使得存在 x 1 <x 2 <……<x m 且 a[x 1 ]<a[x 2 ]<……<a[x m ] 样例输入: 7 1 7 3 5 9 4 8 样例输出: 4. 动态规划解法. L[i] 表示从 1 到 i 这一段中以 i 结尾的最长上升子序列的长度 初始时设 L[i]=0,i=1,2…length(a)

Download Presentation

程序设计实习

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 程序设计实习 习题讲解 李超 lichaopku@gmail.com

  2. 最长上升子序列(ai2757) 问题描述: 给出一个由n个数组成的序列a[1..n],找出它的最长单调上升子序列。即求最大的m,使得存在x1<x2<……<xm且a[x1]<a[x2]<……<a[xm] 样例输入: 7 1 7 3 5 9 4 8 样例输出: 4

  3. 动态规划解法 L[i]表示从1到i这一段中以i结尾的最长上升子序列的长度 初始时设L[i]=0,i=1,2…length(a) 递推方程:L[i] = max{1, L[j]+1}, j=1,2,…i-1, 且a[j]<a[i]

  4. 动态规划解法 例: 1 7 3 5 9 4 8 1 2 1 1 2 2 1 2 2 3 1 2 3 4 2 2 2 4 3 1 3 1 2 2 3 4 3 4

  5. 动态规划解法 复杂性分析: 时间复杂度O(n2) O(n)=O(n-1)+n-2 L[i] = max{1, L[j]+1},j=1,2,…i-1, 且a[j]<a[i] 空间复杂度O(n)

  6. 二分法 思路: 开辟一个栈,每次取栈顶元素s和读到的元素a做比较 如果a>s,则加入栈; 如果a<s,则二分查找栈中的比a大的第1个数p,并用a替换p。  最后序列长度为栈的长度。  时间复杂度O(nlogn)

  7. 二分法 举例: Stack 1 3 7 5 4 8 9

  8. 二分法 该方法只能用于求最长递增子序列的长度,不能求子序列的具体值。 举例:1 5 9 4结果:1 4 9 长度3

  9. 木材加工(ai2774) Time Limit: 1000ms Memory limit: 65536kB 题目描述 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头,计算能够得到的小段木头的最大长度。如果连1厘米长的小段都切不出来,输出"0"。 样例输入 3 7 (1≦N ≦10000, 1≦K ≦10000) 232 (1≦ L1,……Ln ≦10000) 124 456 样例输出 114

  10. 动态规划解法 L[i]表示第 i 个木头的长度,用p[i][j]表示把前i个木头截为j段所能到的最大长度 1)当 L[i]<=p[i-1][j]时,p[i][j]=p[i-1][j] 2) 当 L[i]> p[i-1][j]时, p[i][j]=max{min(p[i-1][j-1],L[i]),min(p[i-1][j-2],L[i]/2),……min(p[i-1][0],L[i]/j)}另外,p[0][0]=0 ,p[0][1]=L[0], p[0][2]=L[0]/2,……P[0][k]=L[0]/k; 时间复杂度O(k2n) ,TLE

  11. 二分法 bool isok(int len);//判断切割K段每段长len是否可行left=0; right=最长木棍的长度;while(left<right-1){ mid=(right+left)/2; if(isok(mid)) left=mid; else right=mid;} 时间复杂度O(nlogM) , M表示最长木棍的长度

  12. 最短前缀(ai2797) 题目描述: 一个字符串的前缀是从该字符串的第一个字符起始的一个子串。例如“carbon”的前缀是: “c”,“ca”,“car”,“carb”,“carbo”, 和“carbon”。现在给你一组单词, 要求你找到唯一标识每个单词的最短前缀。样例: carbohydrate carboh cart cart carburetor carbu caramel cara caribou cari carbonic carboni cartilage carti carbon carbon carriage carr carton carto car car carbonate carbona

  13. 最短前缀(ai2797) 方法1 排序+和前后相邻字符串比较, O(nlogn) 方法2不排序,每个字符串和其他所有字符串比较,O(n2) 方法3 字典树, O(n)

  14. 最短前缀(ai2797) 方法1 排序+和前后相邻字符串比较, O(nlogn) 方法2不排序,每个字符串和其他所有字符串比较,O(n2) 方法3 字典树, O(n)

  15. 字典树(Trie Tree) 用于快速字符串检索的多叉树结构,空间换时间 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 举例:abcd, abc, abd, b, bcd, efg, hi

  16. 用字典树求最短前缀 • char* getPrefix(char * word){ • temp=“”; prefix=“”; isForked=false; • while(1){ • 从根向下行进,temp = temp+c; //c为中间结点的字符值 • ff(isForked==true){ • isForked=false; • prefix=prefix+temp; • temp=“”; • } • if (分叉 or 结点为红色) • isForked==true; • if(路径字符串==word){ • if(是叶结点) • return prefix; • else • return word; • } • } • }

  17. 用字典树求最短前缀 方法2(队列实现) 用”#”表示分叉或者红色结点 if(中间结点) 返回队列全部元素(‘#’除外) if(叶结点) 返回最后一个’#’前的元素+’#’后第一个元素 例: abc bcd

  18. 用字典树求最短前缀 时间复杂度: O(n) 建树 O(n) 求最短前缀 O(n) 空间复杂度: O(n)

  19. 棋盘问题(poj1321) Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。 Input 输入含有多组测试数据。 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为-1 -1时表示输入结束。 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

  20. 棋盘问题(poj1321) Output 对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。 Sample Input 2 1 #. .# 4 4 ...# ..#. .#.. #... -1 -1 Sample Output 2 1

  21. 棋盘问题(poj1321) #include<iostream> #include<cstring> using namespace std; const int MAX = 8; char board[MAX][MAX];//记录棋盘状态 bool placed_c[MAX];//记录一列是否已经放过棋子 int count;//放棋子的方案数 int num_p;//已放棋子数目 int n,k;//棋盘n*n,放的棋子数k

  22. 棋盘问题(poj1321) /* 是否可以放棋子 */ bool can_place(int i,int j){ return !placed_c[j] && board[i][j] == '#'; } /* 深搜/回溯 */ void DFS(int i){ if(num_p == k){ count++; return; } if(n-i<k-num_p) //剪枝,如果剩余行数<剩余棋子数,退出 return; if(i >= n) return; for(int j = 0; j < n; j++){ if(can_place(i,j)){ placed_c[j] = true; num_p++; DFS(i+1); placed_c[j] = false; //j列不放棋子 num_p--; } } //endoffor DFS(i+1);//i行不放棋子,回溯 } //endofDFS

  23. 棋盘问题(poj1321) int main(){ int i,j; while(cin >> n >> k, k != -1){ for(i = 0; i < n; i++) for(j = 0; j < n; j++) cin >> board[i][j]; count = 0; num_p = 0; memset(placed_c,false,sizeof(placed_c)); DFS(0); cout << count << endl; } return 0; }

  24. Thanks! Any question?

More Related