ADAGAME4 Spoj错误答案

时间:2017-12-15 13:38:11

标签: java algorithm dynamic-programming memoization

  

以下是SPOJ的档案PROBLEM。示例testCase正在通过,但我在提交时获得了W / A.我错过了一些testCase(testCases)。需要帮助来弄清楚我错过了什么案例和/或我在这里做错了什么。

     

Ada the Ladybug正和她的朋友Velvet Mite Vinit一起玩Divisors游戏。游戏有以下规则。他们之间有一堆N石头。正在移动的玩家可以选择至少1个最多σ(N)个宝石(其中σ(N)代表N的除数)。显然,每次移动后N都会发生变化。不会得到任何结石(N == 0)的人会输。

Ada瓢虫是一位女士,所以她先行动。你能决定谁将成为赢家吗?假设两个玩家都玩得最好。

输入

  

第一行输入将包含1≤T≤10^ 5,测试用例数。   下一条T线将包含1≤N≤2* 10 ^ 7,这是最初堆积的石块数。

输出

  

输出获胜者的名字,所以“Ada”或“Vinit”。

示例输入:
8
1
3
5
6
11个
1000001个
百万
29

示例输出:
阿达
VINIT
阿达
阿达
VINIT
VINIT
阿达
阿达

代码

import java.io.*;

public class Main
{
    public static int max_size = 2 * (int)Math.pow(10,7) + 1;
    //public static int max_size = 25;
    //public static int max_size = 2 * (int)Math.pow(10,6) + 1;
    public static boolean[] dp = new boolean[max_size];
    public static int[] lastPrimeDivisor = new int[max_size];
    public static int[] numOfDivisors = new int[max_size];
    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        preprocess();

        int t = Integer.parseInt(br.readLine());
        while(t > 0)
        {
            int n = Integer.parseInt(br.readLine());
            if(dp[n] == true)
                System.out.println("Ada");
            else
                System.out.println("Vinit");
            t--;
        }
    }
    public static void markLastPrimeDivisor()
    {
        for(int i = 0 ; i < max_size ; i++)
        {
            lastPrimeDivisor[i] = 1;
        }
        for(int i = 2 ; i < max_size ; i += 2)
        {
            lastPrimeDivisor[i] = 2;
        }
        int o = (int)Math.sqrt(max_size);
        for(int i = 3 ; i < max_size; i++)
        {
            if(lastPrimeDivisor[i] != 1)
            {
                continue;
            }
            lastPrimeDivisor[i] = i;
            if(i <= o)
            {
                for(int j = i * i ; j < max_size ; j += 2 * i)
                {
                    lastPrimeDivisor[j] = i;
                }
            }
        }
        /*for(int i = 1 ; i < max_size ; i++)
            System.out.println("last prime of " + i + " is " + lastPrimeDivisor[i]);*/
    }

    public static void countDivisors(int num)
    {
        int original = num;
        int result = 1;
        int currDivisorCount = 1;
        int currDivisor = lastPrimeDivisor[num];
        int nextDivisor;
        while(currDivisor != 1)
        {
            num = num / currDivisor;
            nextDivisor = lastPrimeDivisor[num];
            if(nextDivisor == currDivisor)
            {
                currDivisorCount++;
            }
            else
            {
                result = result * (currDivisorCount + 1);
                currDivisorCount = 1;
                currDivisor = nextDivisor;
            }
        }
        if(num != 1)
        {
            result = result * (currDivisorCount + 1);
        }
        //System.out.println("result for num : " + original + ", " + result);
        numOfDivisors[original] = result;
    }

    public static void countAllDivisors()
    {
        markLastPrimeDivisor();
        for(int i = 2 ; i < max_size ; i++)
        {
            countDivisors(i);
            //System.out.println("num of divisors of " + i + " = " + numOfDivisors[i]);
        }
    }


    public static void preprocess()
    {
        countAllDivisors();
        dp[0] = dp[1] = dp[2] = true;
        for(int i = 3 ; i < max_size ; i++)
        {
            int flag = 0;
            int limit = numOfDivisors[i];
             //If for any i - j, we get false,for playing optimally
            //the current opponent will choose to take j stones out of the
            //pile as for i - j stones, the other player is not winning.
            for(int j = 1 ; j <= limit; j++)
            {
                if(dp[i - j] == false)
                {
                    dp[i] = true;
                    flag  = 1;
                    break;
                }
            }
            if(flag == 0)
                dp[i] = false;
        }

    }

}

1 个答案:

答案 0 :(得分:1)

countDivisors()功能中存在一个微妙的错误。它假定 lastPrimeDivisor[num] - 如名称所示 - 返回 给定参数的最大主要因素。

但事实并非如此。例如,lastPrimeDivisor[num] = 2 对于所有偶数,或lastPrimeDivisor[7 * 89] = 7。 原因是在

public static void markLastPrimeDivisor()
{
    // ...
    for(int i = 3 ; i < max_size; i++)
    {
        // ...
        if(i <= o)
        {
            for(int j = i * i ; j < max_size ; j += 2 * i)
            {
                lastPrimeDivisor[j] = i;
            }
        }
    }
}

仅更新从i * i开始的数组元素。

所以lastPrimeDivisor[num]实际上是{em> num的一些素数除数,但不是 必然是最大的。结果,计算numOfDivisors[55447] 为8而不是正确的值6。

因此在countDivisors()中,num中的素数因子的指数 必须通过重复划分明确确定。

然后你可以使用除数函数乘法。这导致 以下实施:

public static void countAllDivisors() {

    // Fill the `somePrimeDivisor` array:
    computePrimeDivisors();

    numOfDivisors[1] = 1;
    for (int num = 2 ; num < max_size ; num++) {
        int divisor = somePrimeDivisor[num];
        if (divisor == num) {
            // `num` is a prime
            numOfDivisors[num] = 2;
        } else {
            int n = num / divisor;
            int count = 1;
            while (n % divisor == 0) {
                count++;
                n /= divisor;
            }
            // `divisor^count` contributes to `count + 1` in the number of divisors,
            // now use multiplicative property:
            numOfDivisors[num] = (count + 1) * numOfDivisors[n];
        }
    }
}