关于有向图博弈论SG函数的求法
  • 板块学术版
  • 楼主NirvanaCeleste
  • 当前回复0
  • 已保存回复0
  • 发布时间2024/10/1 21:09
  • 上次更新2024/10/2 08:57:09
查看原帖
关于有向图博弈论SG函数的求法
800543
NirvanaCeleste楼主2024/10/1 21:09
题目描述

Alice和Bob在棋盘上玩游戏。

在一个n∗mn∗m的棋盘上有一颗棋子。为了方便起见,我们用(i,j)表示棋盘上从上到下第i行和从左到右第j列的位置。Alice和Bob约定了一个数字K,对于一个在(i,j)的棋子,他在不走到非法位置的情况下可以走向以下位置:

    (i+1,j)
    (i,j+1)
    (i+x,j+x) (1≤x≤k1≤x≤k)

走到非法位置的情况有:

    i>n
    j>m
    (i,j)是一个禁止通行的位置。

禁止通行的位置信息通过输入给出。

Alice和Bob在棋盘上按照该规则依次移动棋子,Alice先手,谁移动到(N,MN,M)处即获得胜利。有Q次询问,每次询问你假设棋子从某位置开始,并且在Alice和Bob足够聪明的情况下,Alice和Bob谁将获得游戏的胜利?
输入格式

第一行输入三个整数N,M,KN,M,K

其后N行,每行M个字符,如果第i行第j列字符是'.',那么说明该位置可以通行,如果第i行第j列字符是'#',那么说明该位置不能通行。保证该字符矩阵仅包含'.''#'。

接下来一行输入一个整数Q。 接下来Q行,每行两个整数x,yx,y,表示询问假设棋子从(x,y)开始,是先手获胜还是后手获胜。
输出格式

对于每次询问,输出一行。若先手获胜,输出"First",否则输出"Second"

code

//https://henanoi.com/p/17?tid=66f267fb2b043c91198c163e
#include <bits/stdc++.h>
using namespace std;
const int maxn = 330;
char mymap[maxn][maxn];// 时间复杂度 O(nmk)
int sg[maxn][maxn];
bool b[maxn][maxn];
int n,m,k;
int mex(long long x){//不属于这个集合的最小值
	long long t = x & (-x);// lowbit 最低位 1
	int cnt = 0;
	if(t == 0) return 0;//t == 0即为空集合 mex可以为0 
	if(t == 1) while(x & (1 << cnt)) cnt++;//倒着找 
	if(t > 1){
		while(t) t >>= 1,cnt++;//数t是第几位(1到n位) 也就是数0到n-1 往后一个数还要减1 
		cnt -= 2;	
	}
	return cnt;
}
int SG(int i,int j){
	if(i > n || j > m || mymap[i][j] == '#') return 0;
	if(i == n && j == m) return 0;
	if(b[i][j]) return sg[i][j];
	long long temp = 0;//sg函数一般不会太大 
	if(mymap[i+1][j] != '#' && i + 1 <= n && j <= m) temp = temp | (1 << SG(i+1,j));
	if(mymap[i][j+1] != '#' && i <= n && j + 1 <= m) temp = temp | (1 << SG(i,j+1));
	for(int c=1;c<=k;c++) if(i + c <= n && j + c <= m) if(mymap[i+c][j+c] != '#') temp = temp | (1 << SG(i+c,j+c));
	b[i][j] = 1;
	return sg[i][j] = mex(temp);
}
int main(){
//	for(int i=0;i<=10;i++) cout<<i<<' '<<mex(i)<<endl;
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>mymap[i][j];
		}
	}
	sg[n][m] = 0;
	int t,tx,ty;
	cin>>t;
	while(t--){
		cin>>tx>>ty;
		if(SG(tx,ty) == 0) cout<<"Second"<<endl;
		//注意这里是 SG(tx,ty) == 0 而不是 sg[i][j] == 0,因为前面可能不会遍历到后面的 
		else cout<<"First"<<endl;
	}
	return 0;
}

怎样使它的时间复杂度更小? Time Exceeded

样例数据

input

2 2 0
.#
..
2
1 1
2 1

output

Second
First

input

2 2 1
..
..
1
1 1

output

First

input

3 4 0
....
.#..
....
1
3 2

output

Second

数据规模与约定

对于所有数据:

    2≤N≤300 2≤N≤300
    K≥0 K≤N−1
    保证(N,M)处可以通行
    保证从任意位置开始都将棋子移动到(N,M)
    1≤Q≤300 对于1≤i≤Q1,保证1≤xi≤N,1≤yi≤M

2024/10/1 21:09
加载中...