砍柴——欧拉筛

砍柴——欧拉筛

题目描述

小蓝和小乔正在森林里砍柴,它们有 T 根长度分别为 n1, n2, · · · , nT 的木头。对于每个初始长度为 n 的木头,小蓝和小乔准备进行交替砍柴,小蓝先出手。每次砍柴时,若当前木头长度为 x ,需要砍下一截长度为 p 的木头,然后换另一个人继续砍,其中 2 ≤ p ≤ x 且 p 必须为质数。当轮到某一方时 x = 1 或x = 0 ,它就没法继续砍柴,它就输了。它们会使用最优策略进行砍柴。请对每根木头判断是小蓝赢还是小乔赢,如果小蓝赢请输出 1 (数字 1),如果小乔赢请输出 0 (数字 0)。

输入格式

输入的第一行包含一个正整数 T,接下来 T 行,每行包含一个正整数,其中第 i 的整数为 ni 。

输出格式

输出 T 行,每行包含一个整数,依次表示对于每一根木头的答案。

样例输入

3
1
2
6

样例输出

0
1
1

解题思路

采用动态规划的算法 dp[i]表示长度为ii的木头情况下的输赢情况,dp[i]==false表示小蓝赢。

状态转移方程为dp[i]=!dp[i-prime]prime是砍去长度,是不超过当前木头长的质数。质数获取采用欧拉筛获取。

代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] woods = new int[n];
        int maxWood=0;
        for(int i = 0; i < n; i++){
            int val= scanner.nextInt();
            maxWood = Math.max(maxWood, val);
            woods[i]=val;
        }
        boolean[] dp = new boolean[maxWood+1];
        Arrays.fill(dp, false);
        dp[0]=false;
        dp[1]=false;
        //primes存储最大木头以下的所有质数
        List<Integer> primes = PrimeNumber(maxWood);
        for(int i = 2; i <=maxWood; i++){
            for(int j=MaxPrimeIndex(i,primes); j>=0; j--){
                if(!dp[i-primes.get(j)]){
                    dp[i]=true;
                    break;
                }
            }
        }

        for(int i =0;i<n;i++){
            if(!dp[woods[i]]){
                System.out.println('0');
            }else{
                System.out.println('1');
            }
        }
    }

    //获取不大于N的所有质数集合
    public static List<Integer> PrimeNumber(int n){
        //存储当前的所有质数
        List<Integer> primes = new ArrayList<>();
        //记录当前数是否被筛去
        boolean[] isPrime = new boolean[n+1];
        Arrays.fill(isPrime, true);
        isPrime[1] = true;
        for (int i = 2; i <= n; i++) {
            if(isPrime[i]){primes.add(i);}
            for(Integer prime : primes) {
                if(i*prime >n)break;
                isPrime[i*prime] = false;
                if(i%prime == 0)break;
            }
        }
        return primes;
    }

    public static int MaxPrimeIndex(int n, List<Integer> primes){
        int left=0,right=primes.size()-1;
        int mid=0;
        while(left<right){
            mid=(left+right)/2;
            if(primes.get(mid)>n){
                right=mid-1;
            }else if(primes.get(mid)<n){
                left=mid+1;
            }else
                return mid;
        }
        if(primes.get(right)>n) return right-1;
        return right;
    }
}
posted @ 2025-04-03 14:45  狐狸胡兔  阅读(55)  评论(0)    收藏  举报