ZOJ 2725 SG函数 DP

来源:互联网 发布:c高级编程 编辑:程序博客网 时间:2024/06/06 01:57

给一个长度为6的有前导零的数字,每次可以使一位减少,最少减少1,最多减少到0,也可以在0右边的数字和0本身都删除,两人轮流操作,问先手是否必胜

一开始看错题,写了半天都不对,一种是SG函数的,发现SG函数vis数组范围是SG的选择,另一种是顺推的DP,DP要快很多

SG函数 1680ms

#include <set>#include <cmath>#include <queue>#include <stack>#include <string>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef  long long LL;const double PI = acos(-1.0);template <class T> inline  T MAX(T a, T b){if (a > b) return a;return b;}template <class T> inline  T MIN(T a, T b){if (a < b) return a;return b;}const int N = 111;const int M = 11111;const LL MOD = 1000000007LL;const int dir[4][2] = {1, 0, -1, 0, 0, -1, 0, 1};const int INF = 0x3f3f3f3f;int sg[10][10][10][10][10][10][10];int cg(int l, int a[10]){    int b[10], i, j, k;    int & ans = sg[l][a[0]][a[1]][a[2]][a[3]][a[4]][a[5]];    if (l == 0)    {        ans = 0;        return 0;    }    bool v[61];    memset(v, false, sizeof(v));    if (ans != -1) return ans;    for (i = 0; i < l; ++i)    {        if (a[i] != 0)        {            for (j = 1; j <= a[i]; ++j)            {                a[i]-=j;                k = cg(l, a);                v[k] = 1;                a[i]+=j;            }        }        if (a[i] == 0)        {            for (j = i; j < l; ++j)            {                b[j] = a[j];                a[j] = 0;            }            k = cg(i, a);            v[k] = 1;            for (j = i; j < l; ++j)                a[j] = b[j];        }    }    i = 0;    while (v[i]) i++;    ans = i;//    printf("%d %d %d %d %d %d %d: %d\n", l, a[0], a[1], a[2], a[3], a[4],a[5], ans);    return ans;}int main(){    char str[10];    memset(sg, -1, sizeof(sg));    while (scanf("%s", str) != EOF)    {        int l, num[6], i, j, k;        l = strlen(str);        memset(num, 0, sizeof(num));        for (i = 0; i < l; ++i)        {            num[i] = str[i] - '0';        }        if (num[0] == 0) {printf("Yes\n"); continue;}        if (cg(l, num) != 0)printf("Yes\n");        else printf("No\n");    }    return 0;}


 

DP 30ms

#include <set>#include <cmath>#include <queue>#include <stack>#include <string>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef  long long LL;const double PI = acos(-1.0);template <class T> inline  T MAX(T a, T b){if (a > b) return a;return b;}template <class T> inline  T MIN(T a, T b){if (a < b) return a;return b;}const int N = 111;const int M = 11111;const LL MOD = 1000000007LL;const int dir[4][2] = {1, 0, -1, 0, 0, -1, 0, 1};const int INF = 0x3f3f3f3f;bool dp[1111111];int callen(int x){    int m = 0;    while (x)    {        x /= 10;        m++;    }    return m;}void solve(int x){    int q, lim, m = x, l, bas = 1, i, j, k;    l = callen(m);    for (i = 0; i < l; ++i)    {        lim = (m / bas) % 10; q = m;        for (j = 0; j < 9 - lim ; ++j)        {            q += bas;            dp[q] = 1;        }        bas *= 10;    }    if (l < 6)    {        k = 6 - l; m = x;        bas = 1;        for (j = 0; j < k; ++j)        {            m *= 10;            for (i = 0; i < bas; ++i)                dp[m + i] = 1;            bas *= 10;        }    }}void init(){    memset(dp, false, sizeof(dp));    dp[0] = true;    for (int i = 1; i <= 999999; ++i)    {        if (!dp[i]) solve(i);    }}int main(){    char str[10];    init();    while (scanf("%s", str) != EOF)    {        int n, l;        l = strlen(str);        if (str[0] == '0')        {            printf("Yes\n");            continue;        }        n = 0;        for (int i = 0; i < l; ++i)            n = n * 10 + str[i] - '0';        if (dp[n]) printf("Yes\n");        else printf("No\n");    }    return 0;}