card
problem:
有一个3行4列的棋盘
0 1 2 3
4 5 6 7
8 9 10 11
接着依次给出对手的6张卡牌和己方的6张卡牌,编号为0-5,6-11,双方轮流下牌。
最后给出已经下了的卡牌编号和棋盘位置,如果没有默认己方先手。
如:
0 5
7 3
表示已经下了两张牌,这局对手先开局,对手先把0号牌下在棋盘的5号位,己方将7号牌下在3号位。
每张牌依次给出 北 西 南 东 四个属性值,不超过20。
在一次落子后,进行4次判定,如果这个牌的北方向有牌,且该牌暂时属于对方,且这张牌的 北 属性 严格大于上方牌的 南 属性,那么上方(北方)的牌划归下牌者所有,其余方向依此类推。下一张牌后,最多可以改周围4张的归属。
已经下的牌是固定了的,之后的操作双方均绝顶聪明。
己方的得分是最后的归属牌数-6
请输出己方的最大得分,以及下一张牌会下的位置(自己或对手),要求字典序最小。并输出这张卡牌的四维属性。
示例:
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
3 9
输出:
6
6 5
2 2 2 2
solution:
基本的bruteforce是min-max交替搜索。量级在\((12!)*(6!)*(6!)*12\approx 3*10^{15}\)左右。
剪枝一:
有力的剪枝。
考虑一种合法的答案序列,它的最终得分必然是搜索树上所有min节点的最小值,和所有max节点的最大值。所以在向下搜索的时候,由于祖先链上已有的兄弟节点的返回值约束,会要求该节点返回的值要在一个开区间(L, R)内。
以min节点为例,子树返回值>=R continue掉。因为这是个取min的节点,这棵子树相当于没有作用。如果出现了<=L 的子树,说明不合法,直接返回-inf,让上层的max节点忽略它。
如果min节点的所有子树返回值均>=R ,那么返回R即可。
如果某棵子树在合法区间内,那么可以压缩这个合法区间再搜下一颗子树。
(注意由于返回值的不同,即使开区间内没有合法取值,也不能直接return)
max节点的操作同理。
剪枝二:
用时大概优化到原来的70%。
假设当前得分是now_score,以max节点(己方出牌)为例。最好情况是,自己剩余的所有手牌四维属性均大于对方手牌,最差情况是自己全是弱牌。用当前得分+极限情况尝试剪枝。
当前的棋盘归属是一个\(3^{12}\)的地图,每个位置只有0(对手棋子)/1(己方棋子)/-1(未下)三种状态,标记为s。f[s]表示自己全是强牌的时候,能获取的最大得分(在当前分数基础上的increasement)。g[s]表示自己全是弱牌的时候,对方会获取的最小得分(对方的increasement)。f, g数组都是大于等于0的值。
对于一个\(s->t\)的地图转移,显然有\(f[s]=max\{eat(s->t)+g[t]\}, g[s]=min\{f[t]\}\),eat是在选择了一个位置后,吃掉对方的子数。
在搜索的时候,以max节点(己方下棋)为例,它的最终得分被固定在\(now\_score-g(s')\)和\(now\_score+f(s)\)之间。其中s是自己视角下的地图,\(s'\)是对手视角的地图(01翻转)。
对于min节点,\(final\_score \in [now\_score - f(s'), now\_score + g(s)]\)。
通过判断 可能的最终得分 和 合法取值 的区间交,若无交集可以提前退出子树搜索。
f, g数组需要dp预处理,时间在\(3^{12}*12^2\)左右。
Result:
-O3优化,1000次随机数据测试,最慢为158.366s。
#include <bits/stdc++.h>
using namespace std;
#define P pair<char, char>
#define mp(x, y) make_pair(x, y)
vector< P > oper_list;
const int len = 3;
const int wide = 4;
const int _size = len * wide;
const int _num = _size + (_size&1);
const int half = (_num >> 1);
const int _dir = 4;
const int maxs = 531441;
char f[maxs], g[maxs];
int rev[maxs];
char step_forward[_size][_dir];
char board[_size], sum_board = 0;
char belong[_size], sum_belong = 0;
bool used[_num];
char card[_num][_dir];
int next_k = -1, next_x = -1;
char input_size = 0;
char now_player = 0;
inline void Extend(int s,char *_map) {
for (int i=0; i<_size; i++) _map[i] = s%3-1, s/=3;
}
inline int Mark(char *_map) {
int s=0;
for (char i=_size-1; i>=0; i--) s = (s<<1) + s + (_map[i]+1);
return s;
}
inline int Rev(char *_map) {
for (int i=0; i<_size; i++) if (_map[i] != -1) _map[i] = 1 - _map[i];
return Mark(_map);
}
char Dp(char *p, int s) {
if (p[s] != -1) return p[s];
if (p == f) {
f[s] = 0;
char _map[_size], new_map[_size];
Extend(s, _map);
for (char x=0; x<_size; x++) if (_map[x] == -1) {
for (int i=0; i<_size; i++) new_map[i] = _map[i];
new_map[x] = 1;
char score = 0;
for (char u=0; u<_dir; u++) {
char move_to = step_forward[x][u];
if (move_to == -1 || new_map[move_to] != 0) continue;
++score;
new_map[move_to] = 1;
}
int t = Mark(new_map);
f[s] = max(f[s], (char)(score + Dp(g, t)));
}
}
else {
g[s] = 100;
char _map[_size], new_map[_size];
Extend(s, _map);
for (char x=0; x<_size; x++) if (_map[x] == -1) {
for (int i=0; i<_size; i++) new_map[i] = _map[i];
new_map[x] = 0;
int t = Mark(new_map);
g[s] = min(g[s], Dp(f, t));
}
if (g[s] == 100) g[s] = 0;
}
return p[s];
}
inline void Preprocess() {
for (int i=0; i<_size; i++){
if (i < wide) step_forward[i][0] = -1;
else step_forward[i][0] = i - wide;
if (i % wide == 0) step_forward[i][1] = -1;
else step_forward[i][1] = i - 1;
if (i + wide < _size) step_forward[i][2] = i + wide;
else step_forward[i][2] = -1;
if (i % wide == wide - 1) step_forward[i][3] = -1;
else step_forward[i][3] = i + 1;
}
memset(f, -1, sizeof(f));
memset(g, -1, sizeof(g));
char tmp_map[_size];
for (int s=0; s<maxs; s++) {
Dp(f, s);
Dp(g, s);
Extend(s, tmp_map);
rev[s] = Rev(tmp_map);
}
}
inline void Simulate(char k, char x) {
oper_list.push_back( mp(k, x) );
board[x] = k;
bool cur = (k >= half? 1: 0);
sum_board += cur;
belong[x] = cur;
sum_belong += cur;
used[k] = true;
for (int u=0; u<_dir; u++) if (step_forward[x][u] != -1) {
char move_to = step_forward[x][u];
if (board[move_to] != -1) {
char p = board[move_to];
char &val = belong[move_to];
if (val != cur) {
char v = (u >= 2? u-2: u+2);
if (card[k][u] > card[p][v]) {
sum_belong -= val;
val = cur;
sum_belong += cur;
}
}
}
}
}
inline void Initialize() {
freopen("chess.in", "r", stdin);
for (int i=0; i<_num; i++)
for (int j=0; j<_dir; j++) {
int val = 0;
cin >> val;
card[i][j] = val;
}
memset(board, -1, sizeof(board));
memset(belong, -1, sizeof(belong));
memset(used, false, sizeof(used));
int k = -1, x = -1;
cin >> k >> x;
while (k >= 0) {
Simulate(k, x);
k = -1, x = -1;
cin >> k >> x;
}
fclose(stdin);
input_size = oper_list.size();
}
inline void Reverse(char k, char x, char *tmp_belong, char tmp_sum) {
oper_list.pop_back();
board[x] = -1;
if (k >= half) --sum_board;
for (int i=0; i<_size; i++) belong[i] = tmp_belong[i];
sum_belong = tmp_sum;
used[k] = false;
}
char Dfs(char cur, char min_limit, char max_limit) {
char now_score = sum_belong - sum_board;
if (oper_list.size() == _size) return now_score;
char invalid = (cur? 100: -100);
int s = Mark(belong);
int t = rev[s];
if (cur) {
if (now_score - g[t] >= max_limit) return invalid;
if (now_score + f[s] <= min_limit) return min_limit;
}
else {
if (now_score + g[s] <= min_limit) return invalid;
if (now_score - f[t] >= max_limit) return max_limit;
}
char top_score = -invalid;
char enemy = (cur == 0? half: 0);
char tmp_sum = sum_belong;
char tmp_belong[_size];
for (int i=0; i<_size; i++) tmp_belong[i] = belong[i];
for (char k=cur; k< cur + half; k++) if (!used[k])
for (char x=0; x<_size; x++) if (board[x] == -1) {
Simulate(k, x);
char score = Dfs(enemy, min_limit, max_limit);
Reverse(k, x, tmp_belong, tmp_sum);
if (cur) {
if (score >= max_limit) return invalid;
if (score <= min_limit) continue;
min_limit = score;
if (score > top_score) {
top_score = score;
if (oper_list.size() == input_size) next_k = k, next_x = x;
}
}
else {
if (score <= min_limit) return invalid;
if (score >= max_limit) continue;
max_limit = score;
if (score < top_score) {
top_score = score;
if (oper_list.size() == input_size) next_k = k, next_x = x;
}
}
}
if (top_score == -invalid) {
if (cur) return min_limit;
else return max_limit;
}
return top_score;
}
int main(void) {
Preprocess();
Initialize();
now_player = ((oper_list.size() > 0) && (oper_list.rbegin()->first >= half)? 0: half);
clock_t start = clock();
int ans = Dfs(now_player, -100, 100);
clock_t end = clock();
//cout << (end - start) / 1000000.0 << endl;
cout << ans << endl;
cout << next_k << " " << next_x << endl;
for (int i=0; i<_dir; i++) cout << (int)card[next_k][i] << " ";
return 0;
}

浙公网安备 33010602011771号