今天做的福州赛区区域赛的题目重现,一整场都在抠这道题仍然无法AC,时间卡的很紧,不过其实也是自己的搜索学的实在太差,紫书上刷的最少的就是第七章的题 。
我一开始就看出了这道题需要IDA*算法,但是昨天才看的还没能深入理解,通过赛后补这道题,感觉整体思路有了一个新的突破 。
IDA*算法就是迭代加深搜索和A*算法的结合,迭代加深搜索非常简单,就是从小到大枚举深度上限,适合求解深度未知的或者像该题一样需要求最小迭代次数的题目 。
A算法的精髓是写一个估价函数, 如何写呢? 其实就是一个剪枝,通过计算预估一个比较松的下界,也就是预估从当前到达目标至少还要递归多少层,如果它加上当前深度d大于最大深度maxd,应当剪枝 。 事实上,IDA*算法的剪枝函数一般格式也都是这样的 : d + h() > maxd 时剪枝 。有时候也不必严格的在代码里写出h()函数,只是要想清楚在什么情况下不可能在当前深度限制下出解即可 。
另一个非常重要的问题是如何对递归程序进行优化 。其实在场上我已经写出了估价函数,但是仍然超时 ,其原因就是我递归的太多了 。大家都知道如果扩展一颗完整的解答树,那么时间复杂度将极其可怕 ,而我写的暴力程序如果去掉估价函数,就是一颗完整的解答树 。
事实上我做了很多无用的递归 , 大家可以想象一棵树,如果在第一开始就递归一个无用的分支,那么将会对造成极大的浪费 。
我是在每一层枚举6种颜色,都染上看看结果,其实答案只可能是染和当前连通块相邻的颜色(当前连通块就是和左上角元素相连的相同颜色的块)。 所以我们不妨在每一层里加个循环来找与之相邻的颜色,只递归他们 。 看似我们在每一层多加了个循环,浪费了时间,可是和次方级的解答树相比可以忽略不计了~因为我们剪掉了很多树枝 。
这给了我们大家一个启示: 宁可在每一层递归中多跑两个循环,也要想办法减少无用的递归 !
大家可以刷刷紫书上的埃及分数和编辑书稿 ,都很经典 。当然,我的第七章是刷的最少的,也是时候该补补了~
细节见代码:
~~~
#include<bits/stdc++.h>
using namespace std;
const int maxn = 15;
int n,maxd,a[maxn][maxn];
int vis[maxn][maxn];
int dx[] = { 0,1,0,-1,1,-1,-1,1 };
int dy[] = { 1,0,-1,0,1,-1,1,-1 };
void dfs2(int r,int c,int col) { //更新vis[i][j]数组,给vis[i][j] == 2的变成1,与之相邻的变成2
vis[r][c] = 1;
for(int i=0;i<4;++i) {
int x = r+dx[i];
int y = c+dy[i];
if(x < 0 || x >=n || y < 0 || y >= n ) continue;
if(vis[x][y] == 1 ) continue;
vis[x][y] = 2;
if(a[x][y] == col)
dfs2(x,y,col);
}
}
int H() {
int maze[8],cnt = 0;
memset(maze,0,sizeof(maze));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(vis[i][j]==1) continue;
if(!maze[a[i][j]]) { maze[a[i][j]]++; cnt++; }
}
}
return cnt;
}
int filled(int col) {
int cnt = 0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(a[i][j] != col) continue;
if(vis[i][j] == 2) { //只有当vis[i][j] == 2且与col同色的才染色
cnt++;
dfs2(i,j,col);
}
}
return cnt;//返回染色的个数
}
bool dfs(int d) {
if(d == maxd) return H()==0;
if( ( H() + d ) > maxd ) return false;
int vi[maxn][maxn];
memcpy(vi,vis,sizeof(vi));
for(int i=0;i<6;i++) {
if(filled(i) == 0) continue; //没有符合要求的i颜色的块与之相邻
if(dfs(d+1)) return true;
memcpy(vis,vi,sizeof(vis));
}
return false;
}
int main() {
while(~scanf("%d",&n)&&n) {
for(int i=0;i<n;++i)
for(int j=0;j<n;++j) scanf("%d",&a[i][j]);
memset(vis,0,sizeof(vis));
dfs2(0,0,a[0][0]);
for(maxd = 0; ; ++maxd)
if(dfs(0)) break;
printf("%d\n",maxd);
}
return 0;
}
~~~
- 前言
- 1608 - Non-boring sequences(折半递归。。暂且这么叫吧)
- 11491 - Erasing and Winning(贪心)
- 1619 - Feel Good(高效算法-利用数据结构优化-优先队列)
- hdu-4127 Flood-it!(IDA*算法)
- UESTC 1132 酱神赏花 (用数据结构优化DP)
- HDU 2874 Connections between cities(LCA离线算法)
- Codeforces Round #317 A. Lengthening Sticks(组合+容斥)
- HDU 3085 Nightmare Ⅱ(双向BFS)
- HDU 5592 ZYB&#39;s Premutation(二分+树状数组)
- Codeforces Round #320 (Div. 1) C. Weakness and Poorness(三分)
- HDU 5212 Code(容斥)
- HDU 5596 GTW likes gt(multiset)
- FZU 2159 WuYou(贪心)
- HDU 3450 Counting Sequences(DP + 树状数组)
- HDU 5493 Queue(二分+树状数组)
- HDU 1166 敌兵布阵(线段树版)
- HDU 1394 Minimum Inversion Number(树状数组||线段树)
- HDU 2795 Billboard(线段树)
- POJ 2828 Buy Tickets(树状数组)
- 《完全版线段树》- NotOnlySuccess
- POJ 2886 Who Gets the Most Candies?(树状数组+二分)
- HDU 1698 Just a Hook(线段树区间修改)
- POJ 3468 A Simple Problem with Integers(线段树|区间加减&amp;&amp;区间求和)
- POJ 2528 Mayor&#39;s posters(线段树区间修改+离散化)
- HDU 5606 tree(并查集)
- POJ 3734 Blocks(矩阵优化+DP)
- POJ 3233 Matrix Power Series(矩阵优化)
- HDU 5607 graph(矩阵优化+概率DP)
- POJ 2777 Count Color(线段树区间修改+位运算)
- POJ 1436 Horizontally Visible Segments(线段树区间修改)
- UVA 1513 - Movie collection(树状数组)
- UVA 1232 - SKYLINE(线段树区间更新)
- 11525 - Permutation(二分+树状数组)
- 11402 - Ahoy, Pirates!(线段树区间更新(标记重叠的处理))
- Educational Codeforces Round 6 E. New Year Tree(DFS序+线段树)