1393: [Ceoi2008]knights

题意:

在一块N*N的棋盘上有K个骑士,它们只能朝四个方向移动,每一回合,玩家必须把所有能动的骑士移动一格,当一个玩家没有任何骑士可以移动的时候它就输了,问是否先手必胜,如果是,给出一种可行的每个棋子的第一步的方案。


solution:

这是一个every-sg游戏,对于每个局面,如果先手必胜,那么先手必定是想尽可能地多走几步,否则,先手肯定是想尽可能早地结束这场游戏。

那么定义stepi,为局面i执行最优决策时的步数。对于所有局面,先手必胜,当且仅当先手必胜局中step的最大值大于先手必败局中step的最大值,证明很显然的。。。

最后打表找找这张图中sg值和step值的规律就行了

先看先手必败点的分布,总结一下sg值,总结一下step即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 2E5 + 20;
const int INF = ~0U>>1;
const int dx[4] = {-2,-2,-1,1};
const int dy[4] = {1,-1,-2,-2};
 
struct Point{
    int x,y; Point(){}
    Point(int x,int y): x(x),y(y){}
}p[maxn];
 
int n,m,Win,Lose;
 
inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
char s[10];
inline void Print(int x)
{
    int len = 0;
    while (x) s[++len] = x % 10,x /= 10;
    for (int i = len; i; i--) putchar(s[i] + '0');
}
 
inline int min(const int &x,const int &y) {return x < y ? x : y;}
inline int max(const int &x,const int &y) {return x > y ? x : y;}
 
inline int Sg(int x,int y)
{
    if (n % 4 == 0 && x == n && y == n) return 0;
    if (n % 4 == 1 && x == n && y != n - 1) return 0;
    if (n % 4 == 1 && x != n - 1 && y == n) return 0;
    if ((x % 4 == 1 || x % 4 == 2) && (y % 4 == 1 || y % 4 == 2)) return 0;
    return 1;
}
 
inline int LoseStep(int x,int y)
{
    return (x + y - ((x == n || y == n) ? 2 : 1)) / 4 * 2;
}
 
inline int WinStep(int x,int y)
{
    int ret = 0;
    for (int i = 0; i < 4; i++)
    {
        int xx = x + dx[i],yy = y + dy[i];
        if (xx < 1 || xx > n || yy < 1 || yy > n) continue;
        if (Sg(xx,yy)) continue;
        ret = max(ret,LoseStep(xx,yy));
    }
    return ret + 1;
}
 
void Print_Ans()
{
    for (int i = 1; i <= m; i++)
        Print(p[i].x),putchar(' '),Print(p[i].y),puts("");
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
    {
        int x = getint(),y = getint();
        int sg = Sg(x,y),tmp;
        if (sg)
        {
            tmp = -1;
            for (int j = 0; j < 4; j++)
            {
                int xx = x + dx[j],yy = y + dy[j];
                if (xx < 1 || xx > n || yy < 1 || yy > n) continue;
                if (Sg(xx,yy)) continue;
                int now = LoseStep(xx,yy);
                if (now > tmp) p[i] = Point(xx,yy),tmp = now;
            }
            Win = max(Win,tmp + 1);
        }
        else
        {
            tmp = INF;
            for (int j = 0; j < 4; j++)
            {
                int xx = x + dx[j],yy = y + dy[j];
                if (xx < 1 || xx > n || yy < 1 || yy > n) continue;
                int now = WinStep(xx,yy);
                if (now < tmp) p[i] = Point(xx,yy),tmp = now;
            }
            Lose = max(Lose,tmp + 1);
        }
    }
    if (Win < Lose) puts("NO");
    else puts("YES"),Print_Ans();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值