Jan 04, 2016
最长上升子序列 (ai2757)
问题描述:给出一个由n个数组成的序列 a[1..n],找出它的最长单调上升子序列。即求最大的m,使得存在x1<x2<……<xm 且 a[x1]<a[x2]<……<a[xm]
样例输入: 7 1 7 3 5 9 4 8
样例输出:4
动态规划解法
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]
动态规划解法
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
动态规划解法
复杂性分析:
时间复杂度 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)
二分法
思路 :开辟一个栈,每次取栈顶元素 s 和读到的元素a 做比较如果 a>s,则加入栈;如果 a<s,则二分查找栈中的比 a 大的第 1个数 p,并用 a 替换 p。 最后序列长度为栈的长度。 时间复杂度 O(nlogn)
二分法
举例:
1 7 3 5 9 4 8
1
73
8
5
Stack
9
4
二分法
该方法只能用于求最长递增子序列的长度,不能求子序列的具体值。
举例 :1 5 9 4 结果 :1 4 9 长度 3
木材加工 (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
动态规划解法
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
二分法
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表示最长木棍的长度
最短前缀 (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
最短前缀 (ai2797)
方法 1
排序 +和前后相邻字符串比较 , O(nlogn)
方法 2
不排序,每个字符串和其他所有字符串比较, O(n2)
方法 3
字典树 , O(n)
最短前缀 (ai2797)
方法 1
排序 +和前后相邻字符串比较 , O(nlogn)
方法 2
不排序,每个字符串和其他所有字符串比较, O(n2)
方法 3
字典树 , O(n)
字典树 (Trie Tree)
用于快速字符串检索的多叉树结构,空间换时间从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 举例: abcd, abc, abd, b, bcd, efg, hi
用字典树求最短前缀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;
}}
}
用字典树求最短前缀方法 2(队列实现 )用” #”表示分叉或者红色结点if(中间结点 )
返回队列全部元素 (‘#’除外 )if(叶结点 )
返回最后一个’ #’前的元素 +’#’后第一个元素例 : abc
bcd
a b # c
b # c d
用字典树求最短前缀
时间复杂度 : O(n)
建树 O(n) 求最短前缀 O(n)
空间复杂度: O(n)
棋盘问题 (poj1321)Description在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k 个棋子的所有可行的摆放方案C 。Input输入含有多组测试数据。 每组数据的第一行是两个正整数, n k,用一个空格隔开,表示了将在一个 n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为 -1 -1时表示输入结束。 随后的 n行描述了棋盘的形状:每行有 n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
棋盘问题 (poj1321)Output对于每一组数据,给出一行输出,输出摆放的方案数目 C (数据保证 C<2^31)。Sample Input2 1 #. .# 4 4 ...# ..#. .#.. #... -1 -1 Sample Output2 1
棋盘问题 (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
棋盘问题 (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
棋盘问题 (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; }
Thanks!
Any question?