Project Euler 第51题 Prime digit replacements

这里写图片描述
题目来源ProjectEuler

这个题先定义并解释了 x prime value family,指的是一个素数,通过将若干个数字相同的位替换为其他数字,得到的最多10个数中,仍然为素数的这些数组成的集合,比如13,将1替换成其他数字,得到23,33,43,53,63,73,83,93,加上13总共9个数,其中有13,23,43,53,73,83这六个数是素数,这就构成了一个6 prime value family,而13就是所有数里面最小的可以得到6 prime value family的素数,题中的56**3则是另一个例子。

如果这个题完全考虑大暴力的话,复杂度会非常高,但是可以通过一些剪枝的方法来大幅度降低复杂度。

我们参照题目中描述 563 56 ∗ ∗ 3 的方式,用 ∗ ∗ 表示用于替换的位。

首先,由于必须要构成8个素数,所以考虑到十进制下3的倍数是各位数字和为3的倍数的数,那么如果 的个数不是3的倍数个,则构成的10个数中必然会存在至少1个数是3的倍数,因为这10个数构成了等差数列,而在一个长度为10的等差数列中,mod3=0的数至少有3个,那么我们就无法选取8个素数了。所以答案中一定存在至少3个相同的数字作为可替换位,并且我们在计算过程中,只会替换3的倍数个位,再作验证。

其次,我们考虑答案中在可替换位上的数字,而答案数字一定是一组数中最小的那个,所以这只能是0或者1或者2,因为3456789只有七个数。

至于上界,并不容易确定,我先是选择了100w,因为在小于100w的数中,最多只有5个可替换位,这样只需要替换3个,我还没想好用什么方法替换3的任意倍数的数字,所以这里就选择了3个for循环来实现组合。其余的内容基本都是大模拟了。

代码如下:

#include <bits\stdc++.h>
using namespace std;

const int maxn=1000001;
int num[maxn],prime[maxn],cnt;

void calNum(int x,int *cal){//计算各个数字出现的次数
    while(x){
        cal[x%10]++;
        x/=10;
    }
}

bool isRight(int x,int digit,int cal){//判定该数是否正确
    int num1[10],len=0;
    while(x){
        num1[len++]=x%10;
        x/=10;
    }
    for (int a=0;a<cal;a++){
    for (int b=a+1;b<cal;b++){
    for (int c=b+1;c<cal;c++){//排列组合确定需要替换的位
        int tot=1;//family大小的计数器
        for (int i=digit+1;i<=9;i++){
            int num2=0;
            for (int j=len-1,th=0;j>=0;j--){
                num2*=10;
                if (num1[j]!=digit) num2+=num1[j];
                else{
                    if (th==a||th==b||th==c)    num2+=i;
                    else                        num2+=num1[j];
                    th++;
                }
            }
            if (num[num2]==0)    tot++;
        }
        if (tot>=8) return true;
    }
    }
    }
    return false;
}

int main(){
    for (int i=2;i<maxn;i++){//欧拉筛求素数
        if (!num[i])    prime[cnt++]=i;
        for (int j=0;j<cnt&&prime[j]*i<maxn;j++){
            num[i*prime[j]]=1;
        }
    }
    int cal[10];
    for (int i=0;i<cnt;i++){
        memset(cal,0,sizeof(cal));
        calNum(prime[i],cal);
        for (int j=0;j<3;j++){
            if (cal[j]>=3&&isRight(prime[i],j,cal[j])){
                printf("%d\n",prime[i]);
                return 0;
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值