砍柴——欧拉筛
砍柴——欧拉筛
题目描述
小蓝和小乔正在森林里砍柴,它们有 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;
}
}

浙公网安备 33010602011771号