zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:取石子(博弈论+DP)

    题目描述

    有三堆石子,它们的石子个数分别为$x,y,z$。
    $A$和$B$正在博弈,由$A$先手,双方轮流操作。
    每次操作是指,选择若干堆($1-3$堆)石子,从中各取出相同数量的石子(不能$1$个都不取)。不能操作的人失败。
    请判定是否先手必胜。


    输入格式

    第一行一个整数$T$,表示数据组数。
    接下来$T$行,每行三个整数$x,y,z(1leqslant x,y,zleqslant 300)$,描述一组数据。


    输出格式

    每组数据输出一行:
    $ullet$若先手必胜,输出$Yes$,否则输出$No$


    样例

    样例输入:

    2
    1 1 1
    1 2 3

    样例输出:

    Yes
    Yes


    数据范围与提示

    样例解释:

    第一组数据,先手可以一次把所有石子取完。

    第二组数据,先手第一步可以取完第三堆石子,得到$(1,2,0)$是一个先手必败的局面,从而刚开始的先手必胜。

    数据范围:

    对$100\%$的数据,$Tleqslant 500$,记$M=max(x,y,z)$。
    $ullet$子任务$1$($10$分):保证$Mleqslant 7$。
    $ullet$子任务$2$($30$分):保证$Mleqslant 50$。
    $ullet$子任务$3$($30$分):保证$min(x,y,z)=0$。
    $ullet$子任务$4$($30$分):保证$Mleqslant 300$。


    题解

    这是一个$DP$……

    首先,设$dp[i][j][k]$表示第一堆有$i$个,第二堆有$j$个,第三堆有$k$个是否必胜。

    根据博弈论思想,如果一个局面可以转移为一个必败局面,那么这个局面必胜;注意反之则不然,因为我们可以不转移向必胜的局面。

    初始时将所有局面都置为负,然后从小到大枚举$i,j,k$,如果当前局面没有标记胜,则一定为负,然后将所有能转移到它的局面置为胜即可。

    看似时间复杂度是$Theta(n^4)$的,但是注意只有在负的情况下我们才枚举所有能转移到它的局面,而负的局面只有$64972$,所以还是能很快的跑过去的。

    时间复杂度:$Theta(n^3+64972 imes n+T)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    char ans[301][301][301];
    void pre_work()
    {
    	int res;
    	for(int i=0;i<=300;i++)
    		for(int j=0;j<=300;j++)
    			for(int k=0;k<=300;k++)
    			{
    				if(ans[i][j][k])continue;
    				for(int l=i+1;l<=300;l++)ans[l][j][k]=1;
    				for(int l=j+1;l<=300;l++)ans[i][l][k]=1;
    				for(int l=k+1;l<=300;l++)ans[i][j][l]=1;
    				res=300-max(i,j);
    				for(int l=1;l<=res;l++)ans[i+l][j+l][k]=1;
    				res=300-max(i,k);
    				for(int l=1;l<=res;l++)ans[i+l][j][k+l]=1;
    				res=300-max(j,k);
    				for(int l=1;l<=res;l++)ans[i][j+l][k+l]=1;
    				res=300-max(i,max(j,k));
    				for(int l=1;l<=res;l++)ans[i+l][j+l][k+l]=1;
    			}
    }
    int main()
    {
    	pre_work();
    	int T;scanf("%d",&T);
    	while(T--)
    	{
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		puts(ans[x][y][z]?"Yes":"No");
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    HDU 2594 扩展kmp模板题
    HDU 1358 简单kmp
    HDU 3336 扩展kmp
    SPOJ SUBLEX 求第k小子串
    Codeforces 235C
    HDU 4622 Reincarnation
    HDU 4622 求解区间字符串中的不同子串的个数
    [LeetCode] Length of Last Word 字符串查找
    [LeetCode] Sudoku Solver 解数独,递归,回溯
    [LeetCode] Longest Common Prefix 字符串公有前序
  • 原文地址:https://www.cnblogs.com/wzc521/p/11743539.html
Copyright © 2011-2022 走看看