Top Banner
程程程程程程 习习习习 程程 [email protected]
24

程序设计实习

Jan 04, 2016

Download

Documents

Claire Crawford

程序设计实习. 习题讲解 李超 [email protected]. 最长上升子序列 (ai2757). 问题描述: 给出一个由 n 个数组成的序列 a[1..n] ,找出它的最长单调上升子序列。即求最大的 m, 使得存在 x 1
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 程序设计实习

程序设计实习习题讲解

李超

[email protected]

Page 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

Page 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]

Page 4: 程序设计实习

动态规划解法

t L[1] L[2] L[3] L[4] L[5] L[6] L[7]

1

2

3

4

5

6

7

例 :1 7 3 5 9 4 8

1

1

1

1

1

1

1

2

2

2

2

2

2

2

2

2

2

2

3

3

3

3

4

4

4

3

3 4

Page 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)

Page 6: 程序设计实习

二分法

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

Page 7: 程序设计实习

二分法

举例:

   1 7 3 5 9 4 8

1

73

8

5

Stack

9

4

Page 8: 程序设计实习

二分法

该方法只能用于求最长递增子序列的长度,不能求子序列的具体值。

举例 :1 5 9 4    结果 :1 4 9 长度 3

Page 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

Page 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

Page 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表示最长木棍的长度

Page 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

Page 13: 程序设计实习

最短前缀 (ai2797)

方法 1

排序 +和前后相邻字符串比较 , O(nlogn)

方法 2

不排序,每个字符串和其他所有字符串比较, O(n2)

方法 3

字典树 , O(n)

Page 14: 程序设计实习

最短前缀 (ai2797)

方法 1

排序 +和前后相邻字符串比较 , O(nlogn)

方法 2

不排序,每个字符串和其他所有字符串比较, O(n2)

方法 3

字典树 , O(n)

Page 15: 程序设计实习

字典树 (Trie Tree)

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

Page 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;

}}

}

Page 17: 程序设计实习

用字典树求最短前缀方法 2(队列实现 )用” #”表示分叉或者红色结点if(中间结点 )

返回队列全部元素 (‘#’除外 )if(叶结点 )

返回最后一个’ #’前的元素 +’#’后第一个元素例 : abc

bcd

a b # c

b # c d

Page 18: 程序设计实习

用字典树求最短前缀

时间复杂度 : O(n)

建树 O(n) 求最短前缀 O(n)

空间复杂度: O(n)

Page 19: 程序设计实习

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

Page 20: 程序设计实习

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

Page 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

Page 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

Page 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; }

Page 24: 程序设计实习

Thanks!

Any question?