棋局评估(csp 201803-4)
题目类型:博弈论、dfs、最大最小搜索
思路一:去模拟下棋,Alice和Bob轮流下棋,每次使用"最优策略"来决定下在哪里。
“最优策略”为:首先看自己能不能赢,即看有没有已经两个连线再下一个棋子就可以获胜的地方,定义为函数to_win;然后看对方是否将要赢,即看对方有没有已经两个连线再下一个棋子就可以获胜的地方,此时下在这里把它堵住,定义为函数to_lose。最后这两种情况都没有时,由于自己在脑海里模拟了所谓的可以放在两条线的交点上以让另一方堵不住,但是这是难以实现的,因此我就定义了一个关于棋盘上点的优先级,经过该点的线越多,优先级越高。当快要赢和快要输都不存在时,便根据选择空着的点中优先级最高的。
最终得分:45
我将这题的难点归结为最优策略的设置,并苦于无法将自己下棋的一套经验完全表述出来。可笑的是,当我看到题解竟然是用dfs深搜出来的,瞬间觉得自己好傻。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int line[8];//存储各个线的状态
vector<pair<int, int >> Dot[8];
int map[3][3];
int space;
int T;
vector<pair<int, int >> priority;
pair<int, int> res;
int score;
void init_dot() {
priority.push_back(make_pair(1, 1));
priority.push_back(make_pair(0, 0));
priority.push_back(make_pair(2, 2));
priority.push_back(make_pair(0, 2));
priority.push_back(make_pair(2, 0));
priority.push_back(make_pair(0, 1));
priority.push_back(make_pair(1, 0));
priority.push_back(make_pair(1, 2));
priority.push_back(make_pair(2, 1));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Dot[i].push_back(make_pair(i, j));
}
}
for (int i = 3; i < 6; i++) {
for (int j = 0; j < 3; j++) {
Dot[i].push_back(make_pair(j, i-3));
}
}
for (int i = 0; i < 3; i++) {
Dot[6].push_back(make_pair(i, i));
Dot[7].push_back(make_pair(i, 2-i));
}
}
void init_line(int x, int y, int status) {
int effe;
if (status == 1) effe = 1;
else effe = -1;
line[x] += effe;
line[3 + y] += effe;
if (x == y) line[6] += effe;
if (x + y == 2) line[7] += effe;
}
int to_win(int status) {
int standard;
if (status == 1) standard = 2;
else standard = -2;
for (int i = 0; i < 8; i++) {
if (line[i] == standard) {
for (int j = 0; j < 3; j++) {
if (map[Dot[i][j].first][Dot[i][j].second] == 0) {
res.first = Dot[i][j].first;
res.second = Dot[i][j].second;
return 1;
}
}
}
}
return 0;
}
int to_lose (int status) {
int standard;
if (status == 1) standard = -2;
else standard = 2;
for (int i = 0; i < 8; i++) {
if (line[i] == standard) {
for (int j = 0; j < 3; j++) {
if (map[Dot[i][j].first][Dot[i][j].second] == 0) {
res.first = Dot[i][j].first;
res.second = Dot[i][j].second;
return 1;
}
}
}
}
return 0;
}
int judge() {
for (int i = 0; i < 8; i++) {
if (line[i] == 3) {
score = space + 1;
return 1;
}
if (line[i] == -3) {
score = -(space + 1);
return 1;
}
}
if (space == 0) {
score = 0;
return 1;
}
return 0;
}
int main() {
cin >> T;
init_dot();
while (T--) {
space = 0;
memset(line, 0, sizeof(line));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> map[i][j];
if (!map[i][j]) space++;
else init_line(i, j, map[i][j]);
}
}
int status = 1;
while (1) {
if (judge()) {
cout << score << endl;
break;
}
//to win
if (to_win(status)) {
map[res.first][res.second] = status;
init_line(res.first, res.second, status);
space--;
}
else if (to_lose(status)) {
map[res.first][res.second] = status;
init_line(res.first, res.second, status);
space--;
}
else {
//在九个当中选择
for (int i = 0; i < 9; i++) {
if (!map[priority[i].first][priority[i].second]) {
map[priority[i].first][priority[i].second] = status;
init_line(priority[i].first, priority[i].second,status);
space--;
break;
}
}
}
if (status == 1) status = 2;
else status = 1;
}
}
}
思路二:深度优先搜索来模拟所有下棋的过程,最后取得一个分数最高的。
需要注意的是,对于Bob来说,其分数最高代表的反而是最小的,因此需要使用min,而不是max。因此在某种程度上,这也是最大最小搜索。
#include<iostream>
using namespace std;
#define INF 1e7
int map[3][3];
int T;
int check(int status) {
for (int i = 0; i < 3; i++) {
int num = 0;
for (int j = 0; j < 3; j++) {
if (map[i][j] == status) num++;
}
if (num == 3) return 1;
}
for (int i = 0; i < 3; i++) {
int num = 0;
for (int j = 0; j < 3; j++) {
if (map[j][i] == status) num++;
}
if (num == 3) return 1;
}
if (map[0][0] == status && map[1][1] == status && map[2][2] == status) return 1;
if (map[0][2] == status && map[1][1] == status && map[2][0] == status) return 1;
return 0;
}
int eval() {
int space = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (!map[i][j]) space++;
}
}
if (check(1)) return space + 1;
if (check(2)) return -(space + 1);
if (!space) return 0;
return -1;
}
int dfs(int status) {
int t = eval();
if (t != -1) return t;
if (status == 1) {
int res = -INF;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (map[i][j] == 0) {
map[i][j] = status;
res=max(res,dfs(2));
map[i][j] = 0;
}
}
}
return res;
}
else {
int res = INF;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (map[i][j] == 0) {
map[i][j] = status;
res = min(res, dfs(1));
map[i][j] = 0;
}
}
}
return res;
}
}
int main() {
cin >> T;
while (T--) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> map[i][j];
}
}
cout << dfs(1) << endl;
}
}
Widget Factory(poj 2947)!!!
原题链接
题目类型:同余方程组的高斯消元法
由于本题规定了每一个参数都是在3到9之间,因此可以将其转化为同余方程组的问题。
TLE
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define N 400
#define MOD 7
int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int n,m;
int week(string x) {
if (x == "MON") return 1;
else if (x == "TUE") return 2;
else if (x == "WED") return 3;
else if (x == "THU") return 4;
else if (x == "FRI") return 5;
else if (x == "SAT") return 6;
else if (x == "SUN") return 7;
else cout << "wrong trans" << endl;
return 0;
}
int GCD(int a, int b) {
return !b ? a : GCD(b, a % b);
}
int LCM(int a, int b) {
return a / GCD(a, b) * b;
}
int Gauss(int equ, int var) {//返回自由变元个数 equ代表的是方程的个数,var代表的是参数的个数
/*初始化*/
for (int i = 0; i <= var; i++) {
x[i] = 0;
freeX[i] = true;
}
/*转换为阶梯阵*/
int col = 0;//当前处理的列
int row;//当前处理的行
for (row = 0; row < equ && col < var; row++, col++) {//枚举当前处理的行
int maxRow = row;//当前列绝对值最大的行
for (int i = row + 1; i < equ; i++) {//寻找当前列绝对值最大的行
if (abs(a[i][col]) > abs(a[maxRow][col]))
maxRow = i;
}
if (maxRow != row) {//与第row行交换
for (int j = row; j < var + 1; j++)
swap(a[row][j], a[maxRow][j]);
}
if (a[row][col] == 0) {//col列第row行以下全是0,处理当前行的下一列
row--; //先减去row以使得最后的row是不变的
continue;
}
for (int i = row + 1; i < equ; i++) {//枚举要删去的行
if (a[i][col] != 0) {
int lcm = LCM(abs(a[i][col]), abs(a[row][col]));
int ta = lcm / abs(a[i][col]);
int tb = lcm / abs(a[row][col]);
if (a[i][col] * a[row][col] < 0)//异号情况相加
tb = -tb;
for (int j = col; j < var + 1; j++) {
a[i][j] = ((a[i][j] * ta - a[row][j] * tb) % MOD + MOD) % MOD;
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for (int i = row; i < equ; i++)
if (a[i][col] != 0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp = var - row;//自由变元有var-row个
if (row < var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for (int i = var - 1; i >= 0; i--) {//计算解集
int temp = a[i][var];
for (int j = i + 1; j < var; j++) {
if (a[i][j] != 0)
temp -= a[i][j] * x[j];
temp = (temp % MOD + MOD) % MOD;//取模
}
while (temp % a[i][i] != 0)//外层每次循环都是求a[i][i],它是每个方程中唯一一个未知的变量
temp += MOD;//a[i][i]必须为整数,加上周期MOD 使得能够整除不出现小数
x[i] = (temp / a[i][i]) % MOD;//取模
}
return 0;
}
int main() {
while (~scanf("%d %d",&n,&m)) {
if (!n && !m) break;
memset(a, 0, sizeof(a));
for (int i = 0; i < m; i++) {
int k;
cin >> k;
getchar();
char s[4], f[4];
cin >> s;
getchar();
cin >> f;
string start = s;
string finish = f;
a[i][n] = week(finish) - week(start) + 1;
for (int j = 0; j < k; j++) {
int cur;
cin >> cur;
a[i][cur-1]++;
a[i][cur - 1] %= MOD;
}
}
int res = Gauss(m,n);
if (res == -1) cout << "Inconsistent data." << endl;
else if (res) cout << "Multiple solutions." << endl;
else {
cout << ((x[0] < 3) ? x[0]+7 : x[0]);
// cout << x[0];
for (int i = 1; i < n; i++) cout << " " << ((x[i] < 3) ? x[i] + 7 : x[i]);
cout << endl;
}
}
}
tip:
1.GCD函数是辗转相除法的递归版本