搜索方法窥探
一,经典搜索(深搜和广搜)与剪枝
int a[62]; bool v[62]; int n; bool bingo=0; bool cmp(int a,int b){return a>b?1:0;} bool dfs(int begin,int left,int now,int max){ if(left==now) return 1; for(int i=begin;i<n;i++){ if(v[i]||a[i]>now) continue; v[i]=1; if(a[i]==now &&dfs(0,left-a[i],max,max)) return 1; else if(dfs(begin+1,left-a[i],now-a[i],max)) return 1; v[i]=0; if(now==max||a[i]==now) return 0; while(a[i+1]==a[i]) i++; } return 0; } int main(){ int max,sum; while(scanf("%d",&n)!=EOF){ bingo=0; max=-1,sum=0; if(!n) break; for(int i=0;i<n;i++){ scanf("%d",&a[i]); if(a[i]>max) max=a[i]; sum+=a[i]; } int ans; sort(a,a+n,cmp); memset(v,0,sizeof(v)); for(ans=max;ans<=sum/2;ans++) { if(sum%ans!=0) continue; if(dfs(0,sum,ans,ans)){ printf("%d\n",ans); bingo=1; break; } } if(!bingo) printf("%d\n",sum); } return 0; }
二,迭代加深搜索
//POJ 3921 //给出一张有向图,问最少删除几个点,能使起点到终点的最短距离大于K /* * 先用bfs找到一条最短路,然后枚举删除每个点。删除点的时候,可以用迭代加深。 * 先枚举所有删除K个点的情况,如果无解,则枚举删除K+1个点的情况 */ #define MM 4010 #define MN 100 struct Edge { int x, y, next; } edge[MM]; int head[MN], tot; int n, m, k; bool rem[MN]; //false表示该点没删除,true表示该删除了 int path[MN]; int cpath[MN][MN]; queue<int> que; int ite; //表示迭代次数 int finds; //表示是否找到解 void addline(int u, int v) { edge[++tot].next = head[u]; head[u] = tot; edge[tot].x = u, edge[tot].y = v; } //在有向图中利用广度优先搜索找路 bool bfs() { while (!que.empty()) que.pop(); memset(path, 0, sizeof(path)); que.push(1); while (!que.empty()) { int v = que.front(); que.pop(); for (int i = head[v]; i > 0; i = edge[i].next) { if (!path[edge[i].y] && !rem[edge[i].y]) { que.push(edge[i].y); path[edge[i].y] = edge[i].x; if (edge[i].y == n) return 1; } } } return 0; } void dfs(int deep) { if (finds) return; if (!bfs()) { //如果不可到达,无穷大大于K finds = true; return; } int cnt = 0; int v = n; for (int i = n; v > 1; v = path[v]) cpath[deep][cnt++] = v; if (cnt > k) { finds = true; return; } if (deep > ite) return; //如果深度到了,也不用找了 for (int i = 1; i < cnt; i++) { //尝试删除路径上的点 rem[cpath[deep][i]] = 1; dfs(deep + 1); rem[cpath[deep][i]] = 0; } } int solve() { finds = 0; for (ite = 0; ite <= n; ite++) { memset(rem, 0, sizeof(rem)); dfs(1); if (finds) return ite; } return n; }
三,双向广搜
// POJ 1077 康托展开 双向广搜 #define MAXN 402880 int first[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; unsigned int factorial[11] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; char tran[4]; struct Node { int board[9]; int space; int order; void getOrder() { order = GetPermutationNum(board, 9); } }; int father[2][MAXN]; int ope[2][MAXN]; int dir[4][2] = { { 0, -1 }, { -1, 0 }, { 1, 0 }, { 0, 1 } }; //u, l, r, d bool checked[2][MAXN]; int matchingStatus; int matchingQ; bool BFS(Node begin, Node end) { int x, y, dx, dy; queue<Node> Q[2]; Q[1].push(end); Q[0].push(begin); checked[1][end.order] = 1; checked[0][begin.order] = 1; father[1][end.order] = -1; ope[1][end.order] = -1; father[0][begin.order] = -1; ope[0][begin.order] = -1; while (!Q[0].empty() && !Q[1].empty()) { int next; if (Q[0].empty()) next = 1; else if (Q[1].empty()) next = 0; else { if (Q[0].size() < Q[1].size()) next = 0; else next = 1; } int another = 1 - next; Node head = Q[next].front(); Q[next].pop(); if (checked[another][head.order]) { // 出来的元素曾经在另一队列里面 matchingStatus = head.order; matchingQ = next; return 1; } else { x = head.space % 3; y = head.space / 3; for (int i = 0; i < 4; i++) { if (i + ope[next][head.order] == 3) continue; dx = x + dir[i][0]; dy = y + dir[i][1]; Node tmp = head; if (dx >= 0 && dx < 3 && dy >= 0 && dy < 3) { swap(tmp.board[tmp.space], tmp.board[dy * 3 + dx]); tmp.space = dy * 3 + dx; tmp.getOrder(); if (!checked[next][tmp.order] && solvable(tmp)) { checked[next][tmp.order] = 1; father[next][tmp.order] = head.order; ope[next][tmp.order] = i; Q[next].push(tmp); } } } } } return 0; }
四,AStar
#define MAXN 402880 int first[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; char tran[4]; struct Node { int board[9]; int space; int order; int f, g, h; bool operator <(const Node &tmp) const { return f < tmp.f; } void getOrder() { order = GetPermutationNum(board, 9); } void getF() { f = g + h; } }; void getH(Node &p) { int tmp = 0; for (int i = 0; i < 10; i++) if (p.board[i] != 9) { tmp += abs(p.board[i] % 3 - i % 3) + abs(p.board[i] / 3 - i / 3); } p.h = tmp * 4; // 1 倍会超时 } int father[MAXN]; int ope[MAXN]; int dir[4][2] = { { 0, -1 }, { -1, 0 }, { 1, 0 }, { 0, 1 } }; //u, l, r, d int checked[MAXN]; // 0未访问,1在open,2在close int openDir[MAXN], closeDir[MAXN]; multiset<Node> open, close; multiset<Node>::iterator it; bool BFS(Node end) { int x, y, dx, dy; open.insert(end); checked[end.order] = 1; openDir[end.order] = end.f; father[end.order] = -1; ope[end.order] = -1; while (!open.empty()) { Node head = *open.begin(); if (head.order == 0) { return 1; } open.erase(open.begin()); checked[head.order] = 2; closeDir[head.order] = head.f; x = head.space % 3; y = head.space / 3; for (int i = 0; i < 4; i++) { if (i + ope[head.order] == 3) continue; dx = x + dir[i][0]; dy = y + dir[i][1]; if (dx >= 0 && dx < 3 && dy >= 0 && dy < 3) { Node tmp = head; swap(tmp.board[tmp.space], tmp.board[dy * 3 + dx]); if (!solvable(tmp)) continue; tmp.space = dy * 3 + dx; tmp.getOrder(); tmp.g = head.g + 1; getH(tmp); tmp.getF(); father[tmp.order] = head.order; ope[tmp.order] = i; if (checked[tmp.order] == 0) { open.insert(tmp); checked[tmp.order] = 1; openDir[tmp.order] = tmp.f; } else if (checked[tmp.order] == 1) { if (tmp.f < openDir[tmp.order]) { openDir[tmp.order] = tmp.f; } } else if (checked[tmp.order] == 2) { if (tmp.f < closeDir[tmp.order]) { open.insert(tmp); openDir[tmp.order] = tmp.f; checked[tmp.order] = 1; } } } } } return 0; }
五,DFSID
#define MM 4010 #define MN 100 struct Edge { int x, y, next; } edge[MM]; int head[MN], tot; int n, m, k; bool rem[MN]; //false表示该点没删除,true表示该删除了 int path[MN]; int cpath[MN][MN]; queue<int> que; int ite; //表示迭代次数 int finds; //表示是否找到解 void addline(int u, int v) { edge[++tot].next = head[u]; head[u] = tot; edge[tot].x = u, edge[tot].y = v; } //在有向图中利用广度优先搜索找路 bool bfs() { while (!que.empty()) que.pop(); memset(path, 0, sizeof(path)); que.push(1); while (!que.empty()) { int v = que.front(); que.pop(); for (int i = head[v]; i > 0; i = edge[i].next) { if (!path[edge[i].y] && !rem[edge[i].y]) { que.push(edge[i].y); path[edge[i].y] = edge[i].x; if (edge[i].y == n) return 1; } } } return 0; } void dfs(int deep) { if (finds) return; if (!bfs()) { //如果不可到达,则不用找了 finds = true; return; } int cnt = 0; int v = n; for (int i = n; v > 1; v = path[v]) cpath[deep][cnt++] = v; if (cnt > k) { finds = true; return; } if (deep > ite) return; //如果深度到了,也不用找了 for (int i = 1; i < cnt; i++) { //尝试删除路径上的点 rem[cpath[deep][i]] = 1; dfs(deep + 1); rem[cpath[deep][i]] = 0; } } int solve() { finds = 0; for (ite = 0; ite <= n; ite++) { memset(rem, 0, sizeof(rem)); dfs(1); if (finds) return ite; } return n; }
六.博弈树的极大极小算法与Alpha –beta剪枝
题意:给出一个4*4的棋盘,x和o两人轮流放。先放够连续四个的赢。给定一个局面,下一个轮到x放。问x是否有必胜策略?若有,输出能够赢的该下子的坐标。
思路:
(1)Maxfind(Min):每次放x,若得到的Max大于等于Min,则直接返回Max。
(2)Minfind(Max)类似;
(3)因此,枚举第一个人放的位置,Minfind,返回的是INF则胜利。
#include <iostream> using namespace std; #define INF 0x3fffffff int state[5][5], chess, xi, xj; bool over(int x, int y) { //判断一个局面是否是结束局面 int tot = 0; //横向判断 for (int i = 0; i < 4; i++) if (state[x][i] == 'o') tot++; else if (state[x][i] == 'x') tot--; if (tot == 4 || tot == -4) return 1; tot = 0; //纵向判断 for (int i = 0; i < 4; i++) if (state[i][y] == 'o') tot++; else if (state[i][y] == 'x') tot--; if (tot == 4 || tot == -4) return 1; tot = 0; //主对角线判断 for (int i = 0; i < 4; i++) if (state[i][i] == 'o') tot++; else if (state[i][i] == 'x') tot--; if (tot == 4 || tot == -4) return 1; tot = 0; //辅对角线判断 for (int i = 0; i < 4; i++) if (state[i][3 - i] == 'o') tot++; else if (state[i][3 - i] == 'x') tot--; if (tot == 4 || tot == -4) return 1; return 0; } int minSearch(int, int, int); int maxSearch(int x, int y, int Min) { // printf("max %d\n", Min); int tmp, Max = -INF; if (over(x, y)) return Max; if (chess == 16) return 0; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (state[i][j] == '.') { state[i][j] = 'x'; chess++; tmp = minSearch(i, j, Max); chess--; state[i][j] = '.'; Max = max(Max, tmp); if (Max >= Min) return Max; } return Max; } int minSearch(int x, int y, int Max) { // printf("min %d\n", Max); int tmp, Min = INF; if (over(x, y)) return Min; if (chess == 16) return 0; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (state[i][j] == '.') { state[i][j] = 'o'; chess++; tmp = maxSearch(i, j, Min); chess--; state[i][j] = '.'; Min = min(Min, tmp); if (Min <= Max) return Min; } return Min; } bool solve() { int tmp, Max = -INF; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (state[i][j] == '.') { state[i][j] = 'x'; chess++; tmp = minSearch(i, j, Max); chess--; state[i][j] = '.'; Max = max(Max, tmp); if (Max == INF) { printf("(%d,%d)\n", i, j); return 1; } } return 0; } int main() { // freopen("data3.txt", "r", stdin); char OP; while (scanf("%c", &OP) && OP != '$') { getchar(); chess = -4; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { state[i][j] = getchar(); if (state[i][j] != '.') chess++; } getchar(); } if (chess < 4) { //强力剪枝 printf("#####\n"); continue; } if (!solve()) printf("#####\n"); } return 0; }