poj 1085 Triangle War 1568 Find the Winning Move 極大極小搜尋 alpha-beta剪枝
一,極大極小搜尋及alpha-beta剪枝(參考這裡)
在博弈搜尋中,比如:圍棋,五子棋,象棋等,結果有三種可能:勝利,失敗和平局。
理論上可以窮舉所有的走法,這就需要生成整棵博弈樹。實際上不可行。因此搜尋時可以限制博弈樹的深度,到達該深度則不再往下搜,相當於只往前看n步。
假設:max和min對弈,輪到max走棋了,那麼我們遍歷max的每一個可能走棋方法,然後對於前面max的每一個走棋方法,遍歷min的每一個走棋方法,然後接著遍歷max的每一個走棋方法,。。。直到分出了勝負或者達到了搜尋深度的限制。若達到搜尋深度限制時尚未分出勝負,則根據當前局面的形式,給出一個得分,計算得分的方法被稱為估價函式,不同遊戲的估價函式如何設計和具體的遊戲相關。
在搜尋樹中,輪到max走棋的節點為極大結點,輪到min走棋的節點是極小節點。
極大極小搜尋
確定估價函式,來計算每個棋局結點的估價值,對max方有利,估價值為正,對max越有利,估價值越大;對min方有利,估價值為負,對min方越有利,估價值越小。
從當前棋局的結點要決定下一步如何走時,以當前棋局節點為根,生成一棵深度為n的搜尋樹。不妨總是假設當前棋局結點是max結點。
用局面估價函式計算出每個葉子結點的估價值。
若某個非葉子結點是極大結點,則其估價值為其子節點中估價最大的那個結點的估價值。
若某個非葉子結點是極小結點,則其估價值為其子結點中估價最小的那個結點的估價值。
max選當前棋局結點的估價值最大的那個子節點,作為此步行棋的走法。
虛擬碼如下(
function minimax(node, depth)
if node is a terminal node or depth = 0
return the heuristic value of node
if the adversary is to play at node
let α := +∞
foreach child of node
α := min(α, minimax(child, depth-1))
else {we are to play at node}
let α := -∞
foreach child of node
α := max(α, minimax(child, depth-1))
return α
alpha和beta剪枝
若結點x是min結點,其兄弟結點中,已經求出來的最大估價值是b(有些兄弟結點還沒有算),那麼在對x的子節點進行考察的時候,一旦發現x的某子節點的估價值小於等於b,則不必在考察後邊x的結點了。這裡被剪掉的是x結點的子節點,也就是極大結點。同理對應的還有beta剪枝,剪掉的是極小結點。
二,例題
poj1568 Find the Winning Move
給定一個4*4的四子連珠棋盤的局面,現在輪到x先走,請問x下在哪裡可以必勝。
....
.xo.
.ox.
....
在窮盡搜尋中,而非搜幾層就停止的情況下,估價函式只有三種取值,正(inf),0,負(-inf),分別代表己方勝,平,負。
必勝是無論對手怎麼走,自己最後都能找到獲勝辦法,不是自己無論怎麼走都能獲勝。
如果能找到一個走法,其對應的結點估價值是inf,則就是一個必勝的策略。
具體程式碼如下:
/*************************************************************************
> File Name: 1568.cpp
> Author: gwq
> Mail: [email protected]
> Created Time: 2015年09月01日 星期二 14時41分57秒
************************************************************************/
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())
using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef long long ll;
const double esp = 1e-5;
#define N 5
int ansx, ansy, cnt;
char mp[N][N], tp[N];
int max_search(int x, int y, int beta);
void print(void)
{
for (int i = 0; i < 4; ++i) {
printf("%s\n", mp[i]);
}
}
bool check(int x, int y)
{
bool flag = true;
for (int i = 0; i < 4; ++i) {
if (mp[x][i] != mp[x][y]) {
flag = false;
break;
}
}
if (flag) {
return true;
}
flag = true;
for (int i = 0; i < 4; ++i) {
if (mp[i][y] != mp[x][y]) {
flag = false;
break;
}
}
if (flag) {
return true;
}
int c = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (i + j == x + y) {
if (mp[i][j] == mp[x][y]) {
++c;
}
}
}
}
if (c == 4) {
return true;
}
c = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (i - j == x - y) {
if (mp[i][j] == mp[x][y]) {
++c;
}
}
}
}
if (c == 4) {
return true;
} else {
return false;
}
}
int min_search(int x, int y, int alpha)
{
int ans = INF;
if (check(x, y)) {
return INF;
}
if (cnt == 16) {
return 0;
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (mp[i][j] == '.') {
mp[i][j] = 'o';
++cnt;
int tmp = max_search(i, j, ans);
ans = min(ans, tmp);
mp[i][j]= '.';
--cnt;
if (ans <= alpha) {
return ans;
}
}
}
}
return ans;
}
int max_search(int x, int y, int beta)
{
int ans = -INF;
if (check(x, y)) {
return -INF;
}
if (cnt == 16) {
return 0;
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (mp[i][j] == '.') {
mp[i][j] = 'x';
++cnt;
int tmp = min_search(i, j, ans);
ans = max(ans, tmp);
mp[i][j]= '.';
--cnt;
if (ans >= beta) {
return ans;
}
}
}
}
return ans;
}
bool solve(void)
{
int alpha = -INF;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
if (mp[i][j] == '.') {
mp[i][j] = 'x';
++cnt;
int tmp = min_search(i, j, alpha); // X走過之後O走
mp[i][j] = '.';
--cnt;
if (tmp == INF) {
ansx = i;
ansy = j;
return true;
}
}
}
}
return false;
}
int main(int argc, char *argv[])
{
while (scanf("%s", tp) != EOF) {
if (tp[0] == '$') {
break;
}
cnt = 0;
for (int i = 0; i < 4; ++i) {
scanf("%s", mp[i]);
for (int j = 0; j < 4; ++j) {
if (mp[i][j] != '.') {
++cnt;
}
}
}
if (cnt <= 4) { // 這一步直接從2S+到0ms,然而並不知道為啥
printf("#####\n");
continue;
}
if (solve()) {
printf("(%d,%d)\n", ansx, ansy);
} else {
printf("#####\n");
}
}
return 0;
}
poj 1085 Triangle War
兩個遊戲者輪流填充虛線三角形,每次只能填充一條短邊,若某遊戲者填充一條短邊後組成了一個小三角形,則該遊戲者擁有這個三角形,並且可以繼續填充。
當所有邊都被填充之後,擁有三角形數目多的遊戲者獲勝。給定一個局面,問誰可以贏。
三角形的個數是9,當一個人擁有5個及以上個三角形是這個人就必勝。這裡的估價函式定義為a的三角形數-b的三角形數。
程式碼如下:
/*************************************************************************
> File Name: 1085.cpp
> Author: gwq
> Mail: [email protected]
> Created Time: 2015年09月01日 星期二 16時35分54秒
************************************************************************/
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())
using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef long long ll;
const double esp = 1e-5;
#define N 20
int num[N], who, n, cnt, ans[2];
int dx[] = {1, 1, 2, 2, 2, 3, 3, 4, 5, 4, 4, 5, 5, 6, 6, 7, 8, 9};
int dy[] = {2, 3, 3, 4, 5, 5, 6, 5, 6, 7, 8, 8, 9, 9, 10, 8, 9, 10};
int mp[][N] = {
{0, 1, 2},
{2, 4, 5},
{3, 4, 7},
{5, 6, 8},
{7, 10, 11},
{11, 12, 16},
{8, 12, 13},
{9, 10, 15},
{13, 14, 17}
};
int getid(int a, int b)
{
int aa = min(a, b);
int bb = max(a, b);
for (int i = 0; i < 18; ++i) {
if (aa == dx[i] && bb == dy[i]) {
return i;
}
}
return -1;
}
// 檢查三角形的時候,需要注意,因為加入的時候可能組成兩個三角形
int check(int id)
{
int ret = 0;
for (int i = 0; i < 9; ++i) {
if (mp[i][0] == id || mp[i][1] == id || mp[i][2] == id) {
int flag = 0;
for (int j = 0; j < 3; ++j) {
flag += num[mp[i][j]];
}
if (flag == 3) {
ret++;
}
}
}
return ret;
}
int min_search(int alpha, int a, int b);
int max_search(int beta, int a, int b)
{
if (cnt == 18) {
return a > b ? INF : -INF;
}
if (a >= 5) {
return INF;
}
if (b >= 5) {
return -INF;
}
int tmp = -INF;
for (int i = 0; i < 18; ++i) {
if (!num[i]) {
num[i] = 1;
++cnt;
// 改變後需要恢復,索性存起來
int aa = a;
int bb = b;
int cc = check(i);
aa += cc;
if (cc) {
// 這裡傳入的值需要注意,這個的父節點是max,
// 所以這裡的剪枝需要beta,而不是tmp,即當前
// min結點獲得的最大值。下邊類似
tmp = max(max_search(beta, aa, bb), tmp);
} else {
tmp = max(min_search(tmp, aa, bb), tmp);
}
num[i] = 0;
--cnt;
if (tmp >= beta) {
return tmp;
}
}
}
return tmp;
}
int min_search(int alpha, int a, int b)
{
if (cnt == 18) {
return a > b ? INF : -INF;
}
if (a >= 5) {
return INF;
}
if (b >= 5) {
return -INF;
}
int tmp = INF;
for (int i = 0; i < 18; ++i) {
if (!num[i]) {
num[i] = 1;
++cnt;
int aa = a;
int bb = b;
int cc = check(i);
bb += cc;
if (!cc) {
tmp = min(max_search(tmp, aa, bb), tmp);
} else {
tmp = min(min_search(alpha, aa, bb), tmp);
}
--cnt;
num[i] = 0;
if (tmp <= alpha) {
return tmp;
}
}
}
return tmp;
}
int main(int argc, char *argv[])
{
int t;
int cs = 0;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
clr(num, 0);
clr(ans, 0);
who = 0;
cnt = n;
for (int i = 0; i < n; ++i) {
int a, b;
scanf("%d%d", &a, &b);
int id = getid(a, b);
num[id] = 1;
int cc = check(id);
if (cc) {
ans[who] += cc;
} else {
who = !who;
}
}
int ret = 0;
if (!who) {
ret = max_search(INF, ans[0], ans[1]);
} else {
ret = min_search(-INF, ans[0], ans[1]);
}
printf("Game %d: %c wins.\n", ++cs, ret == INF ? 'A' : 'B');
}
return 0;
}
相關推薦
poj 1085 Triangle War 1568 Find the Winning Move 極大極小搜尋 alpha-beta剪枝
一,極大極小搜尋及alpha-beta剪枝(參考這裡) 在博弈搜尋中,比如:圍棋,五子棋,象棋等,結果有三種可能:勝利,失敗和平局。 理論上可以窮舉所有的走法,這就需要生成整棵博弈樹。實際上不可行。因此搜尋時可以限制博弈樹的深度,到達該深度則不再往下搜,相當
POJ Find the Winning Move【minmax搜索+alpha-beta剪枝】【北大ACM/ICPC競賽訓練】
目前 剪枝 find 最大 namespace row 競賽 move icpc 1 #include<iostream> 2 using namespace std; 3 4 int row,col,chess; 5 char bo
POJ 1085 Triangle War(極大極小搜尋+alpha-beta剪枝)
// // main.cpp // Richard // // Created by 邵金傑 on 16/8/29. // Copyright © 2016年 邵金傑. All rights reserved. // #include<iostream&g
【叠代博弈+搜索+剪枝】poj-1568--Find the Winning Move
() spa class 勝利 hid iat media gif nes poj 1568:Find the Winning Move 【叠代博弈+搜索+剪枝】 題面省略。。。 Input The input contains one or more test c
POJ 1568 Find the Winning Move
blank ble tdi ref return print bsp sca 在哪裏 Find the Winning Move 鏈接 題意: 4*4的棋盤,給出一個初始局面,問先手有沒有必勝策略? 有的話輸出第一步下在哪裏,如果有多個,按
[poj 1568]Find the Winning Move
Find the Winning Move Time Limit: 3000MS Memory Limit: 32768K Total Submissions:1782 Accepted
1568 Find the Winning Move 極小極大搜尋+alpha-beta剪枝
題目:在一個4*4的格子裡面,x和o兩個人玩遊戲,x畫'x',o畫'o',x先手,給定x和o都已經畫了一定步數的局面,問x是不是必勝的,如果是,輸出他應該畫在哪個位置 思路:極小極大搜尋+alpha-beta剪枝 程式碼: #pragma comment(linker, "
poj 1085 Triangle War (狀壓DP+記憶化搜尋+博弈)
Triangle War is a two-player game played on the following triangular grid: Two players, A and B, take turns filling in any dotted li
博弈(alpha-beta 剪枝)POJ —— 1085 Triangle War
Triangle War Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3066 Accepted: 1207 Description Triangle War is a two-pl
POJ 1085 Triangle War(博弈,極大極小搜尋+alpha_beta剪枝)
題目:給出10個點,總共有18條邊,每次兩個人輪流加入一條邊,如果形成一個三角形,則三角形歸他所有,而且可以額外再走一步。最後三角形多的人勝 博弈問題 所謂的極大極小搜尋,其實就是搞個估價函式。然後主角肯定選個估價函式最大的,即對自己最有利的局面走。 而輪到對方的時候,
poj 1085 Triangle War (狀壓+記憶化搜索)
popu for align 技術 min \n most n) let Triangle War Time Limit:?1000MS ? Memory Limit:?655
杭電-1599 find the mincost route(最小環有向圖)
find the mincost route Time Limit: 1000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4164
leetcode 486. Predict the Winner (Alpha-Beta剪枝實現關鍵點小總結)
題意 小的博弈遊戲,兩個人輪流從一個數組的兩端取數,直到取完,最後取的和最大的人獲勝。問先手能否贏?其中如果和相同,先手勝。 思路 首先如果有偶數個元素,先手必勝,這個可以參考leetcode 877題求解思路,證明連結 奇數個的時候就沒有這麼好的
poj 1426 Find The Multiple
lines code span cti its case sig pac unsigned id=1426">poj 1426 的傳送門 Language: Find The Multiple Time Limit: 1000MS Mem
POJ 1426 Find The Multiple && 51nod 1109 01組成的N的倍數 (BFS + 同余模定理)
ase 正整數 ng- eof ger put emp lan respond Find The Multiple Time Limit: 1000MS Memory Limit: 10000K Total Submissio
POJ 1426 Find The Multiple(DFS,BFS)
ons pro sum 數字 there lin queue hat 一個數 Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal
POJ 1426 Find The Multiple(數論——中國同余定理)
定義 十進制 pro desc decimal tput one return solution 題目鏈接: http://poj.org/problem?id=1426 Description Given a positive integer n, write a pro
POJ 1426 -- Find The Multiple
二叉搜索樹 center pan include false esp while 搜索 amp POJ 1426 -- Find The Multiple 大致題意: 給出一個整數n,(1 <= n <= 200)。求出任意一個它的倍數m,要求m必須只由十進制的
poj-1426-Find The Multiple(打表水過)
poj != AI pac IT ostream multipl tdi stream 思路: 2的最近可以整除的數是10 所以,很關鍵的一點,只要是偶數,例如: 6:2*3,可以拆分為能夠被2整除和能夠被3整除的乘積,所以,10*111=1110 144:72*2
POJ 1426 Find The Multiple(大數取模)【DFS】||【BFS】
++ printf true pty ace bfs 還要 ems 兩種 <題目鏈接> 題目大意: 給一個小於200的正整數n,問只有0和1組成的位數小於100的最小能被n整除的數是多少。 解題分析: 用DFS或者BFS沿著位數進行搜索,每一次搜索到下一位都有兩