博弈树-BIT
下棋属于一种博弈游戏,博弈过程可以用树(博弈树)来表示。假设游戏由两个人( A 和 B )玩,开始由某个人从根结点开始走,两个人轮流走棋,每次只能走一步, 下一步棋只能选择当前结点的孩子结点,谁先走到叶子结点为胜。例如,对于下图所示的博弈树,若 A 先走,可以选 f , B 若选 h ,则 A 选 j 胜。
编写一程序,让计算机和人下棋。当计算机走下一步时,可以根据以下情况决定下一步:
(1) 若存在可以确保取胜的一个孩子结点,则选择该结点作为下一步;
(2) 若存在多个可以确保取胜的孩子结点,则选择其中高度最小的结点作为下一步(若有多个选择,则选最左边的结点);
(3) 若不存在可以确保取胜的一个孩子结点,则选择高度最大的孩子结点作为下一步(若有多个选择,则选最左边的结点);
例: (下面的黑体为输入)
(a,(b,(x)),(c,(d),(e,(g),(h)),(f)))
a
b
x
c
d
e
g
h
f
Who play first(0: computer; 1: player )?
1
player:
c
computer: d
Sorry, you lost.
Continue(y/n)?
y
Who play first(0: computer; 1: player )?
1
player:
x
illegal move.
player:
b
computer: x
Sorry, you lost.
Continue(y/n)?
y
Who play first(0: computer; 1: player )?
0
computer: c
player:
f
Congratulate, you win.
Continue(y/n)?
n
大体思路:
先根据给出的广义表结构创立广义表,再根据建立的广义表创立树(这里采用了孩子兄弟表示法),关于建立广义表和根据广义表的步骤请参考《数据结构》教材,这里不加赘述。
下棋嘛,就是两个人的事,这里就是人和电脑的事。
人所下的位置是由输入决定的,对它的孩子节点进行遍历就能找到下一个位置,只要处理好非法输入的情况就好了。
而对于电脑,我们就需要做到题目要求的三点,
(1)对于电脑来说,非胜即败,通过预处理让电脑知道哪个点是必胜点,那个点是必败点,清楚了结果之后再 玩游戏,就可以开始“欺负”人类了。(叶子节点是必胜点,它的孩子节点全部为必败点的节点为必胜点,其他节点均视为必败点)
(2)(3)计算节点的高度,必胜点考虑最小高度,必败点考虑最大高度
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>#define OK 1
#define YES 1
#define NO 0
#define ERROR 0
#define MAX_SIZE 10005using namespace std;const int INF = 0x3f3f3f3f;
typedef char ElemType;
typedef char AtomType;
typedef int Status;
typedef long long ll;typedef enum {ATOM, LIST} ElemTag;
typedef struct GLNode {ElemTag tag;union {AtomType atom;struct {struct GLNode *hp, *tp;}ptr;};
} * Glist;typedef struct Tnode{ElemType data;int status;struct Tnode * firstchild, * nextsibling;
} * Tree;string emp("()");Status CreateGlist(Glist &L, string s);
Status sever(string &str, string &hstr);
Glist GetHead(Glist L);
Glist GetTail(Glist L);
void CreateTree(Tree &T, Glist L);
Status pre_deal(Tree T);
Status play(Tree T);
int get_height(Tree T);
Status who_play_first();
Status illegal_move();
Tree computer_do(Tree T);
Tree human_do(Tree T);int main() {string s;cin >> s;int i;for(i = 0; s[i] != '\0'; i++) {if(s[i] >= 'a' && s[i] <= 'z') printf("%c\n", s[i]);}Glist L;CreateGlist(L, s);Tree T;CreateTree(T, L);pre_deal(T);play(T);return 0;
}Status CreateGlist(Glist &L, string s) { //建立广义表if(s == emp) {L = NULL;}else {L = new GLNode;if(s.length() == 1) { //原子节点L ->tag = ATOM;L ->atom = s[0];}else { //表结点L ->tag = LIST;string sub, hsub;sub = s.substr(1, s.size() - 2);//cout << sub;Glist p, q;p = L;while(!sub.empty()) {sever(sub, hsub); //分离头尾//cout << sub << endl;//cout << hsub << endl;CreateGlist(p ->ptr.hp, hsub);q = p;if(!sub.empty()) { //若尾表非空,创建尾表指向以便递归使用p = new GLNode;p ->tag = LIST;q ->ptr.tp = p;}}q ->ptr.tp = NULL;} //else 创建表结点} //else 表不空return OK;
}Status sever(string &str, string &hstr) { //头尾分离函数(用于建立广义表)int i, k = 0;int n = str.length();char ch = '\0';for(i = 0; i < n && (ch != ',' || k != 0); i++) {ch = str[i];if(ch == '(') {k++;}if(ch == ')') {k--;}}if(i < n) {hstr = str.substr(0, i - 1);str = str.substr(i, n - i);//cout << hstr << endl;//cout << str << endl;}else {hstr = str;str.clear();}return OK;
}Glist GetHead(Glist L) {if(L) {return L ->ptr.hp;}else {return NULL;}
}Glist GetTail(Glist L) {if(L) {return L ->ptr.tp;}else {return NULL;}
}void CreateTree(Tree &T, Glist L) { //根据广义表建立树if(L) {T = new Tnode;T ->data = GetHead(L) ->atom;T ->firstchild = NULL;T ->nextsibling = NULL;if(GetTail(L)) {Glist h = GetHead(GetTail(L));Glist t = GetTail(GetTail(L));CreateTree(T ->firstchild, h);Tnode * p = T ->firstchild;while(t) {h = GetHead(t);t = GetTail(t);CreateTree(p ->nextsibling, h);p = p ->nextsibling;}p ->nextsibling = NULL;}}else {T = NULL;}return;
}Status play(Tree T) {while(1) {who_play_first();int player, flag = 0;scanf("%d", &player);Tree p = T;switch(player) {case 0:p = computer_do(T);flag = 0;break;case 1:p = human_do(T);flag = 1;break;default: exit(-1);}if(p ->firstchild == NULL) {// 本局结束if(flag == 0) {printf("Sorry,you lost.\n");}else {printf("Congratulate,you win.\n");}printf("Continue(y/n)?\n");char c;getchar();scanf("%c", &c);if(c == 'y') {continue;}else {return OK;}}if(flag == 1 && p != T) { //下一步电脑while(1) { //循环电脑-人-电脑-人-...p = computer_do(p);if(p ->firstchild == NULL) {printf("Sorry,you lost.\n");printf("Continue(y/n)?\n");char c;getchar();scanf("%c", &c);if(c == 'y') {break;}else {return OK;}} while(1) { //处理因输入错误而重来的情况Tree q;q = human_do(p);if(q != p) {p = q;break;}}if(p ->firstchild == NULL) {printf("Congratulate,you win.\n");printf("Continue(y/n)?\n");char c;getchar();scanf("%c", &c);if(c == 'y') {break;}else {return OK;}} }}else { //下一步人while(1) { //思路同上while(1) {Tree q;q = human_do(p);if(q != p) {p = q;break;}}if(p ->firstchild == NULL) {printf("Congratulate,you win.\n");printf("Continue(y/n)?\n");char c;getchar();scanf("%c", &c);if(c == 'y') {break;}else {return OK;}}p = computer_do(p);if(p ->firstchild == NULL) {printf("Sorry,you lost.\n");printf("Continue(y/n)?\n");char c;getchar();scanf("%c", &c);if(c == 'y') {break;}else {return OK;}}}}}return OK;
}Status pre_deal(Tree T) { //预处理if(T) {if(T ->firstchild == NULL) {T ->status = 1;}pre_deal(T ->firstchild);pre_deal(T ->nextsibling);Tnode * p = T;if(T ->firstchild) {p = T ->firstchild;int flag = 1;while(p) {if(p ->status == 1) {flag = 0;break;}p = p ->nextsibling;}if(flag) {T ->status = 1;}else {T ->status = 0;}}}return OK;
}Status who_play_first() {printf("Who play first(0:computer;1:player)?\n");return OK;
}Status illegal_move() {printf("illegal move.\n");return OK;
}Tree computer_do(Tree T) { //电脑进行一轮操作int victory_h = INF, false_h = 0;Tree p = T, q1, q2;if(p ->firstchild) {p = p ->firstchild;while(p) {int h = get_height(p);if(p ->status == 1) {if(h < victory_h) {victory_h = h;q1 = p;}}else {if(h > false_h) {false_h = h;q2 = p;}}p = p ->nextsibling;}if(victory_h != INF) {printf("computer:%c\n", q1 ->data);return q1;}else {printf("computer:%c\n", q2 ->data);return q2;}}return T;
}Tree human_do(Tree T) { //人进行一轮操作printf("player:\n");char c;getchar();scanf("%c", &c);Tnode * p = T;if(p ->firstchild) {p = p ->firstchild;while(p) {if(p ->data == c) {break;}p = p ->nextsibling;}}if(p) {return p;}else {illegal_move();return T;}
}int get_height(Tree T) { //计算树的高度if(T == NULL) return 0;else {int h, h2;h = get_height(T ->firstchild);if(h != 0) {T = T ->firstchild;while(T) {h2 = get_height(T ->nextsibling);if(h < h2) {h = h2;}T = T ->nextsibling;}}return h + 1;}
}