• 【博弈】UVA10561 Treblecross


    这题细节还挺多的。

    分析

    拿到题目先拿样例找一下性质。

    对于最初的局面,发现如果有一个位置出现 X ,那么它的左边两个或者右边两个位置只要存在 X ,那么先手必然胜利。

    如果不存在上面的情况,我们就可以枚举一下先手决策的位置,看看所得到的状态能不能让后手必输。

    这时候我们想到用 sg 函数来刻画这一点。

    方便起见,我们约定 X 的左右两个位置为“禁着点”(记为 O)(因为谁在这个地方下了 X 谁就输了)。

    (f[x]) 表示空棋盘(也就是 (x). )的 sg 函数值。

    我们以 (x=10) 为例(..........):

    当下了一个 X 时在位置 (p=5) 时,棋盘变为 ..OOXOO...

    剩下的部分可以看成两个棋盘,大小分别为 (2, 3)


    一般地,对于落点 (p) ,得到的两个新棋盘的 sg 函数值为 (f[max(0, p-3)],~f[max(0, x-p-2)])

    类似地枚举落点的位置,我们可以得到 (f[x])

    [f[x] = { m mex}_{p=1}^{x} (f[max(0, p-3)]oplus f[max(0, x-p-2)]) ]

    mex(minimal exdudant)函数

    (S) 表示一个非负整数集合。定义 ({ m mex{(S)}}) 为求出不属于集合 (S) 的最小非负整数的运算。
    如:{ (0,1,3) } 对应的 ({ m mex}) 值 就是 (2)


    最后还要提醒一下输出问题:

    • 每一行输出行末不要空格!
    • 如果判断得到的是 LOSING ,要额外输出一个空行。

    高清程序

    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb(a) push_back(a)
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/b
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    typedef long long ll;
    typedef pair<int,int> PII;
    typedef pair<double,double> PDD;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=220;
    
    int n;
    string str, tmp;
    
    vector<int> res;
    
    int f[N];
    
    int sg(int x){
    	if(~f[x]) return f[x];
    	
    	unordered_set<int> buf;
    	rep(i,1,x){ // 枚举决策求 sg 函数
    		int len1=i-3, len2=x-i-2;
    		len1=max(len1, 0), len2=max(len2, 0);
    		int t=sg(len1)^sg(len2);
    		buf.insert(t);
    	}
    	
    	int p=0;
    	while(buf.count(p)) p++;
    	return f[x]=p;
    }
    
    void init(){
    	memset(f, -1, sizeof f);
    	f[0]=0;
    	f[1]=f[2]=f[3]=1;
    	
    	rep(i,1,N-1) sg(i);
    }
    
    int main(){
    	init(); // 预处理出 sg 函数并存在 f[] 中
    	
    	int T; cin>>T;
    	while(T--){
    		res.clear(); // 记得清空 res
    		cin>>str;
    		n=str.size();
    		
    		str="##"+str+"##"; // 减少特判边界,加上边界符号
    		
    		bool ok=false; // 特殊判断一开始的局面是否存在 "XX." "X.X" ".XX" 的情况
    		rep(i,2,n+1) if(str[i]=='.'){
    			bool fl=false;
    			if(str[i-1]=='X' && str[i-2]=='X') ok=fl=true;
    			if(str[i+1]=='X' && str[i+2]=='X') ok=fl=true;
    			if(str[i-1]=='X' && str[i+1]=='X') ok=fl=true;
    			if(fl) res.pb(i);
    		}
    		
    		if(ok){
    			puts("WINNING");
    			rep(i,0,res.size()-1){
    				if(!i) cout<<res[i]-1;
    				else cout<<' '<<res[i]-1;
    			}
    			cout<<endl;
    			
    			continue;
    		}
    		
    		rep(i,2,n+1) if(str[i-2]!='X' && str[i-1]!='X' && str[i]!='X' && str[i+1]!='X' && str[i+2]!='X'){
    			tmp=str; 
    			tmp[i]='X';
    			
    			int t=0;
    			rep(j,2,n+1){
    				if(tmp[j]=='.'){ // 将连续的 "." 区域拿来讨论。
    					int l=j;
    					while(tmp[j]=='.') j++; 
    					j--;
    					int r=j;
    					
    					int len=r-l+1; 
    					if(tmp[l-1]=='X') len-=2; 
    					if(tmp[r+1]=='X') len-=2;
    					len=max(len, 0);
    
    					t^=f[len];
    				}
    			}
    			
    			if(!t) res.pb(i);
    		}
    		
    		if(res.size()){
    			puts("WINNING");
    			rep(i,0,res.size()-1){
    				if(!i) cout<<res[i]-1;
    				else cout<<' '<<res[i]-1;
    			}
    			cout<<endl;
    		}
    		else puts("LOSING
    ");
    	}	
        return 0;
    }	
    
  • 相关阅读:
    [BAT] 以当前时间为名创建文件夹,将本地文件夹里的文件拷贝到远程共享目录,而且保证本地和Jenkins上运行都成功
    [Jenkins] 执行SoapUI的task,设置邮件内容为HTML+CSS
    bzoj 2435 dfs处理
    Gym 100989E 字符串
    Codeforces Beta Round #95 (Div. 2) C 组合数学
    Gym 100989F 水&愚&vector
    Gym 100971C 水&愚&三角形
    Gym 100971B 水&愚
    HDU 5696 RMQ+滑窗
    UVA 1619/POJ2796 滑窗算法/维护一个单调栈
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15072521.html
Copyright © 2020-2023  润新知