UVA1602 Lattice Animals 回溯+set判重
2016-10-16 14:15
10 查看
按照题意,只要我们能找出所有n连块,然后再判断能不能放进w*h的方格中就行了。找出所有n连块可以用回溯法,从k连块搜索,通过在其周围放一个方块,从而得到k+1连块,从而这样不断搜索下去直到n连块,搜索过程的判重就交给set了。因为题目中有多组输入,假如连续输入1000组相同的数据,我们都要一遍一遍的dfs吗?显然太复杂。所以我们可以事先把所有可能的输入数据对应的答案打表,这样输出就快的多了。在打表的时候又面临一个问题了,我们要得到所有的4连块,就要从1连块开始dfs,要得到所有的5连块,也要从1连块开始dfs,这样不是显得太笨拙了吗?同时我们写的这种最简单的枚举方法,使得每一种n连块都被枚举了很多次,所以上面那种笨拙的方法是肯定不能用的,必然会TLE。那么怎么做呢?假如我们要得到所有的5连块,我们就可以通过刚刚已经搜索的4连块得出,这样就简化了搜索过程。开始写代码面临的第一个问题就是如何表示n连块的状态,我们可以把n连块想成是n个坐标的集合,用set实现就可以了。下面是具体实现过程:
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <set> #define INF 0x3f3f3f using namespace std; const int dx[] = {-1, 1, 0, 0};//方向数组 const int dy[] = {0, 0, -1, 1}; int n, w, h; int ans[15][15][15];//所有输入对应的答案 struct cell//定义单元格 { int x, y; cell(int a, int b) { x = a; y = b; } cell(){} bool operator <(const cell &rhs) const { if (x != rhs.x) return x < rhs.x; return y < rhs.y; } }; typedef set<cell> poly;//n个坐标的集合也就是一个n连块 set<poly> vis[15];//用来判重的set,vis 是被访问过的所有n连块的集合 poly initial_normal(const poly &p)//将这个n连块标准化成左上角在(0,0)点 { poly goal; int minx = INF, miny = INF; for (cell it : p) { minx = min(minx, it.x); miny = min(miny, it.y); } for (cell it : p) goal.insert((cell){it.x - minx, it.y - miny}); return goal; } inline poly rotate(const poly &p)//顺时针旋转转90°,千万记住末尾返回的应该是标准化之后的答案 { //因为有的n连块顺时针旋转90°之后出界了,需要将其标准化。 poly goal; //因为这个函数会被执行多次,用inline可以加快执行速度 for(poly::iterator i=p.begin(); i!=p.end(); i++) goal.insert(cell(i->y, -i->x)); return initial_normal(goal); } inline poly flip(const poly &p)//将一个n连块沿x轴翻转,同时也要将答案标准化 { poly goal; for(poly::iterator i=p.begin(); i!=p.end(); i++) goal.insert(cell(i->x,-i->y)); return initial_normal(goal); } bool had_vis(poly p)//判断一个n连块是否访问过,没访问过就插入到vis里面,返回false { int len = p.size(); for (int i=0; i<4; i++) { p = rotate(p); if (vis[len].count(p)) return true; } p = flip(p);//将p翻转之后,再次顺时针旋转看出现过没有 for (int i=0; i<4; i++) { p = rotate(p); if (vis[len].count(p)) return true; } vis[len].insert(p); return false; } void dfs(const poly &p)//由当前k连块dfs到n连块 { if (p.size() == n) { had_vis(p); return; } for (cell cur : p) { for (int d=0; d<4; d++) { cell next(cur.x + dx[d], cur.y + dy[d]); //if(next.x >= 0 && next.x < w && next.y >= 0 && next.y < h && !p.count(next)) //刚开始这里是这样写的,最后答案错误,想了很久才发现。现在的目标不是dfs出 //一个可以装进w*h网格中的n连块,现在的目的是dfs出所有的n连块,所以不在乎能不能装进去 if (!p.count(next)) { poly p_next = p; p_next.insert(next); dfs(p_next); } } } } void print_table()//将所有可能的输入打表 { memset(ans, 0, sizeof(ans)); poly S; S.insert(cell(0, 0));//得到1连块 vis[1].insert(S); for (n=2; n<=10; n++)//首先搜索n = 10的情况,这样搜索一遍后,vis里面 { //就含有了所有大小的连通块。搜索k连块是借用的k-1连块的搜索结果。 for (poly it1 : vis[n - 1]) dfs(it1); } for (n=2; n<=10; n++)//对所有可能打表 for (w=1; w<=10; w++) for (h=1; h<=10; h++) { int cnt=0; for (poly it1 : vis ) { int maxx=0,maxy=0; for (cell it2 : it1)//寻找当前的连通块的最大的x,y { maxx = max(maxx, it2.x); maxy = max(maxy, it2.y); } //能够放入w*h网格内的条件 if(min(maxx, maxy) < min(h, w) && max(maxx, maxy) < max(h, w)) cnt++; } ans [w][h]=cnt; } } int main() { print_table(); while (scanf("%d%d%d", &n, &w, &h)==3) { if (n == 1) {printf("1\n");continue;}//1的时候特判 printf("%d\n", ans [w][h]); } return 0; }
相关文章推荐
- 数据结构与算法 leetcode刷题013 递归和回溯的使用
- 最长公共子序列之回溯
- 计算机中的回溯
- POJ 1040 典型回溯!
- 国际象棋“皇后”问题的回溯算法
- 回溯法与树的遍历求集合的幂子集
- 回溯算法n皇后问题
- 关于回溯法
- 八皇后问题 回溯递归 C语言版
- 回溯法 -- 装载问题
- 基于深度优先的回溯算法框架
- 搜索算法简单介绍:回溯法,深度优先搜索,广度优先搜索
- poj 3278 Catch That Cow 广度优先搜索 + 回溯 队列 剪枝
- pku 2362_Square 深搜+回溯
- 回溯法求解八数码问题
- 小议正则表达式效率 贪婪、非贪婪与回溯
- lv 算法与回溯法相结合解决n皇后问题
- poj 1020 DFS +回溯
- 回溯的价值分析
- 回溯算法理解