常见的搜索算法:记忆化搜索,迭代加深,meet in the middle,A* 和
IDA*
记忆化搜索
思想可以用来进行搜索到DP的优化
建一个备忘录memo[N][S]
memo[i][j]表示在第i个点,状态为j时搜索结果。
或者memo[N]
memo[i]表示第i个点,搜索结果。
上述备忘录初值memo[i]为0,说明该状态未被搜索。
P1278
单词游戏
他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。
游戏可以从任何一个单词开始。任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。
游戏的复杂度定义为游戏中所使用的单词长度总和。
编写程序,求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static int vis; static int[] memo[] = new int[N][1<<N]; static int dfs(int c){ if (memo[c][vis] != 0) return memo[c][vis]; vis |= 1<<c; char next = s[c].charAt(s[c].length()-1); int res = 0; for (int i = 0;i < n;i++){ if ((vis>>i&1)==1 || s[i].charAt(0)!=next) continue; res = max(dfs(i), res); } vis &= ~(1<<c); return memo[c][vis] = res+s[c].length(); }
|
迭代加深
1 2 3 4 5 6 7 8 9
| 从小到大枚举答案所需步数, 然后在搜索时一旦超出这个步数就不再搜索。 根据搜索空间的一般规律,搜索的状态空间随着步数指数级增长。 这样我们的时间主要取决于最后一次搜索的时间, DFS的缺点得到了一定程度的弥补。
迭代加深搜索使用范围: 1.在有一定的限制条件时使用(例如“如果能在1515步以内(包括1515步)到达目标状态,则输出步数“)。 2.题目中说输出所以解中的任何一组解。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static int[] res = new int[N]; public static void main(String[] args) throws Exception { res[1] = 1; if (n == 1) out.println(1); else{ int deep = 2; while (!dfs(2, deep)) deep++; for (int i = 1;i <= deep;i++) out.print(res[i]+" "); out.println(); } } static boolean dfs(int c, int max){ if (c==max+1) return res[c-1]==n; int pre = res[c-1]; boolean s = false; for (int i = c-1;i >= 1 && !s;i--){ int y = pre+res[i]; if (y>pre && y<=n){ res[c] = y; s |= dfs(c+1, max); }else break; } return s; }
|
Addition
Chains
meet in the middle
折半搜索,又称为meet-in-the-middle。其做法为将整个搜索的过程分为两部分,然后每部分分别进行搜索,最后将得到两个答案序列,再将答案序列进行合并,即可得到最终的答案。
我们知道,搜索的时间复杂度往往是指数级别的。
比如,在每一层搜索时,假如都有两种选择,那么其时间复杂度为 O(2n) 。当 n
较大时,往往会导致超时。此时,如果使用折半搜索,其时间复杂度将缩小为
O(2n/2) + 合并时间复杂度)
。
所以当搜索范围n > 40以上时,可优先考虑。
合并常用策略:哈希表、排序加二分
世界冰球锦标赛
给出预算和每场比赛的票价,试求:如果总票价不超过预算,有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static long money, ans; static int len1, len2, len; static long[] p = new long[N], sub1 = new long[1<<21], sub2 = new long[1<<21]; public static void main(String[] args) throws Exception { dfs(1, n/2, 0, sub1); len1 = len; len = 0; dfs(n/2+1, n, 0, sub2); len2 = len; Arrays.sort(sub1, 0, len1); for (int i = 0;i < len2;i++){ int l = 0, r = len1-1; long t = money-sub2[i]; while (l <= r){ int mid = l+r>>1; if (sub1[mid]>t) r = mid-1; else l = mid+1; } ans += l; } println(ans); } static void dfs(int cur, int max, long sum, long[] sub){ if (sum > money) return; if (cur > max){ sub[len++] = sum; return; } dfs(cur+1, max, sum+p[cur], sub); dfs(cur+1, max, sum, sub); }
|
Balanced Cow Subsets
G
A*
用啥A*,IDA*不香吗
IDA*
IDA* =
A* + 迭代加深
IDA*
是对结合迭代加深的DFS的优化。
本质上只是在BFS和DFS上加上了一个估价函数。 ```