1. 程式人生 > >丟史蒂芬妮“盛大遊戲杯”第15屆上海大學程式設計聯賽夏季賽暨上海高校金馬五校賽

丟史蒂芬妮“盛大遊戲杯”第15屆上海大學程式設計聯賽夏季賽暨上海高校金馬五校賽

丟史蒂芬妮

釋出時間: 2017年7月9日 20:20   最後更新: 2017年7月10日 21:11   時間限制: 1000ms   記憶體限制: 128M

有一天,空和白很無聊,決定玩盛大遊戲,考慮到兩個人玩,他們隨便掏了一個遊戲出來:在一個nm的棋盤上,首先把史蒂芬妮·多拉放在左上角(1,1)的位置。每次一個人可以將她往下,往右,往右下丟一格。當前回合,誰不能丟史蒂芬妮,誰就輸了。(注意,不可以把活人丟出棋盤啦!)遊戲總是空先手。

白說,這是一個垃圾遊戲!我們每次把史蒂芬妮丟素數個位置吧!(換句話說,每次丟2357或…格)空答應了。

我們都知道,空和白都很聰明,不管哪方存在一個可以必勝的最優策略,都會按照最優策略保證勝利。

玩了一局,空已經知道了這個遊戲的套路,現在他決定考考你,對於給定的nm,空是贏是輸?如果空必勝,輸出“Sora”(無引號);反之,輸出“Shiro”(無引號)。

第一行有一個T表示陣列組數,1<=T<100000
從第二行開始,每行為棋盤大小,nm分別表示行列。
1=<n<=5001=<m<=500

對於每組資料,按題目要求輸出。

 複製
4
1 1
2 2
10 10
30 30
Shiro
Shiro
Shiro
Sora

資料量很大,建議使用scanf或關閉cin的同步。

記憶化搜尋

可以將問題轉化成取石子:

有兩堆石子,數量分別為n個和m個(n*m的棋盤),每次可以取走其中一堆質數個石子,

也可以同時取走兩堆數量相同的質數個石子,到誰不能取誰失敗

很明顯,(0, 0),(0,1),(1,0)都是必輸態

那麼(2,0),(0,2),(2,1),(0,3),(3,0),(0,3),……,(k,1),(1,k),(k+1,0),(0,k+1)(k為質數)都是必贏態,因為它們都可以一步取到必輸態

知道可以這樣處理就好辦了,因為n和m就500,直接記憶化搜尋預處理

dp[x][y]==0表示(x,y)狀態必輸,dp[x][y]==1表示(x,y)狀態必贏

那麼如果(x,y)的前驅只要有一個是必輸態,那麼(x,y)就是必贏態,否則必輸

程式碼一:

#include<stdio.h>
#include<string.h>
int k=0, dp[505][505], pri[505], jud[505] = {1,1};
int Sech(int x, int y)
{
    int i;
    if(dp[x][y]!=-1)
        return dp[x][y];
    for(i=1;i<=k&&pri[i]<=x;i++)
    {
        if(Sech(x-pri[i], y)==0)
        {
            dp[x][y] = 1;
            return dp[x][y];
        }
    }
    for(i=1;i<=k&&pri[i]<=y;i++)
    {
        if(Sech(x, y-pri[i])==0)
        {
            dp[x][y] = 1;
            return dp[x][y];
        }
    }
    for(i=1;i<=k&&pri[i]<=y&&pri[i]<=x;i++)
    {
        if(Sech(x-pri[i], y-pri[i])==0)
        {
            dp[x][y] = 1;
            return dp[x][y];
        }
    }
    dp[x][y] = 0;
    return dp[x][y];
}
int main(void)
{
    int T, n, i, j, m;
    for(i=2;i<=500;i++)
    {
        if(jud[i])
            continue;
        pri[++k] = i;
        for(j=i*i;j<=500;j+=i)
            jud[j] = 1;
    }
    scanf("%d", &T);
    memset(dp, -1, sizeof(dp));
    while(T--)
    {
        scanf("%d%d", &n, &m);
        dp[0][0] = dp[1][0] = dp[0][1] = dp[1][1] = 0;
        if(Sech(n-1, m-1))
            printf("Sora\n");
        else
            printf("Shiro\n");
    }
    return 0;
}

程式碼二:

#include<bits/stdc++.h>
using namespace std;
const int maxn= 500;
int prime[maxn+5]= {0};
int num_prime=0;
bool isprime[maxn+5]= {1,1};
int vis[maxn+5][maxn+5];
void init()
{
    for(int i=2; i<maxn+5; i++)
    {
        if(!isprime[i])
            prime[num_prime++]=i;
        for(int j=0; j<num_prime&&i*prime[j]<maxn+5; j++)
        {
            isprime[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
    memset(vis,0,sizeof(vis));//1:必勝 2:必敗
    vis[1][1]=2;
    for(int i=1; i<=maxn; i++)
    {
        for(int j=1; j<=maxn; j++)
        {
            if(vis[i][j]==0)
            {
                vis[i][j]=2;
            }
            if(vis[i][j]==2)
            {
                for(int k=0; ; k++)
                {
                    if(i+prime[k]>maxn&&j+prime[k]>maxn)
                        break;
                    if(i+prime[k]<=maxn)
                    {
                        vis[i+prime[k]][j]=1;
                    }
                    if(j+prime[k]<=maxn)
                    {
                        vis[i][j+prime[k]]=1;
                    }
                    if(i+prime[k]<=maxn&&j+prime[k]<=maxn)
                    {
                        vis[i+prime[k]][j+prime[k]]=1;
                    }
                }
            }
        }
    }
}
int main()
{
    init();
    int n,m;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        if(vis[n][m]==1) puts("Sora");
        else puts("Shiro");
    }
    return 0;
}