首页 > 其他分享 >#10024. 「一本通 1.3 练习 3」质数方阵

#10024. 「一本通 1.3 练习 3」质数方阵

时间:2023-04-30 11:11:59浏览次数:49  
标签:10 return 1.3 int 质数 pos 10024 void

loj题目传送门
一本通题目传送门
洛谷传送门
原题是UVA835,是多测

思路

肯定是要剪枝的呀
众所周知,dfs的路径像树一样
显而易见,树的某一层的节点越少,他的下面的分支就越少
于是我们考虑改变搜索顺序来剪掉更多的分支
一个数的末位要是 \(0\),那他肯定不是质数。于是我们先从所有数的末位开填
对角线由于斜着,所以可以影响到更多行和列。于是我们在填对角线
注意:对角线是从左上到右下左下到右上
最后我们要尽可能让某行某列满,再剪掉更多分支(这个可以随便选某行某列,就是选剩空少的就行)
填表顺序如下:

\(1\) \(16\) \(23\) \(18\) \(2\)
\(21\) \(11\) \(24\) \(15\) \(3\)
\(20\) \(17\) \(12\) \(19\) \(4\)
\(22\) \(14\) \(25\) \(13\) \(5\)
\(7\) \(8\) \(9\) \(10\) \(6\)

我们不能等他填完一行/一列/对角线才去检查他是否是质数,这样会很浪费时间
于是剪枝
先枚举出所有的五位质数,用一个五维数组pri[][][][][]来存放
然后我们把质数的某几位用 \(10\) 替换,代表这几位还没填
把替换完的数也标成质数,表示要是当前填成这样有可能是质数
由于我们不是按顺序搜索的,所以最后还需要排个序

代码

#include<bits/stdc++.h>
#define ll long long
#define pc putchar
using namespace std;
template<typename T>
inline void read(T &x)
{
    x=0;
    T f=1;
    char c=getchar();
    while(!isdigit(c)){if(!isspace(c))f=-1; c=getchar();}
    while(isdigit(c)){x=x*10+(c^48); c=getchar();}
    x*=f;
    return;
}
template<typename T,typename... Args> inline void read(T &x, Args&... args){read(x); read(args...);}
template<typename Z>
inline void write(Z x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar((x%10)^48);
}
template<typename Z,typename... Args> inline void write(Z x, Args... args){write(x); putchar(' '); write(args...);}
const int order[27][3] = {//搜索顺序
    {0, 0, 0},
    {0, 1, 1},
    {0, 1, 5},
    {0, 2, 5},
    {0, 3, 5},
    {0, 4, 5},
    {0, 5, 5},
    {0, 5, 1},
    {0, 5, 2},
    {0, 5, 3},
    {0, 5, 4},
    {0, 2, 2},
    {0, 3, 3},
    {0, 4, 4},
    {0, 4, 2},
    {0, 2, 4},
    {0, 1, 2},
    {0, 3, 2},
    {0, 1, 4},
    {0, 3, 4},
    {0, 3, 1},
    {0, 2, 1},
    {0, 4, 1},
    {0, 1, 3},
    {0, 2, 3},
    {0, 4, 3},
};
struct sol{
    int a[6][6];
    void copy(int b[6][6])
    {
        for(int i=1;i<=5;i++)
            for(int j=1;j<=5;j++)
                a[i][j] = b[i][j];
    }
    void out()//输出
    {
        for(int i=1;i<=5;i++)
        {
            for(int j=1;j<=5;j++)
                write(a[i][j]);
            pc('\n');
        }
        pc('\n');
    }
    friend bool operator<(const sol& x, const sol& y){//排序函数
        for(int i=1;i<=5;i++)
            for(int j=1;j<=5;j++)
            	if(x.a[i][j] != y.a[i][j])//相等的话暂时不能返回
					return (x.a[i][j] < y.a[i][j]);//从小到大
    }
};
int sum, k, a[6][6];
bool pri[11][11][11][11][11];
bool is_p(int x)//检验是否是质数
{
    for(int i=2;i*i<=x;i++)
        if(x%i == 0)
            return 0;
    return 1;
}
void cope_prime()
{
    for(int x=10000;x<=99999;x++)//枚举五位质数
        if(is_p(x))
        {
            int p[6], s=x;
            p[5] = s%10; s /= 10;//每位分解出来
            p[4] = s%10; s /= 10;
            p[3] = s%10; s /= 10;
            p[2] = s%10; s /= 10;
            p[1] = s%10;
            for(int i1=0;i1<=1;i1++)
                for(int i2=0;i2<=1;i2++)
                    for(int i3=0;i3<=1;i3++)
                        for(int i4=0;i4<=1;i4++)
                            for(int i5=0;i5<=1;i5++)
                            {
                                int a,b,c,d,e;
                                i1 ? a=p[1] : a=10;//进行替换
                                i2 ? b=p[2] : b=10;
                                i3 ? c=p[3] : c=10;
                                i4 ? d=p[4] : d=10;
                                i5 ? e=p[5] : e=10;
                                pri[a][b][c][d][e] = 1;//标记
                            }
        }
}
bool check(int x,int y)
{
    bool ful = 1;//满没满, 0为有空, 1为满了
    int cnt=0, p[10];//cnt记录和; p[]记录每一位数字, 判断是否是质数
    for(int i=1;i<=5;i++)//列
    {
        if(a[i][y] == -1) ful = 0, p[i] = 10;//有空缺
        else p[i] = a[i][y];//这位有数,记录
        if(ful) cnt += a[i][y];//没有空缺, 累加进和
    }
    if(pri[p[1]][p[2]][p[3]][p[4]][p[5]] == 0) return 0;//不是质数
    if(ful && cnt != sum) return 0;//全填了, 但是和不对

    ful = 1; cnt = 0;
    for(int i=1;i<=5;i++)//行
    {
        if(a[x][i] == -1) ful = 0, p[i] = 10;
        else p[i] = a[x][i];
        if(ful) cnt += a[x][i];
    }
    if(pri[p[1]][p[2]][p[3]][p[4]][p[5]] == 0) return 0;
    if(ful && cnt != sum) return 0;

    if(x == y)//从左上到右下的对角线
    {
        ful = 1; cnt = 0;
        for(int i=1;i<=5;i++)
        {
            if(a[i][i] == -1) ful = 0, p[i] = 10;
            else p[i] = a[i][i];
            if(ful) cnt += a[i][i];
        }
        if(pri[p[1]][p[2]][p[3]][p[4]][p[5]] == 0) return 0;
        if(ful && cnt != sum) return 0;
    }

    if(x+y == 6)//从左下到右上的对角线
    {
        ful = 1; cnt = 0;
        for(int i=5;i>=1;i--)
        {
            int j = 6-i;
            if(a[i][j] == -1) ful = 0, p[j] = 10;
            else p[j] = a[i][j];
            if(ful) cnt += a[i][j];
        }
        if(pri[p[1]][p[2]][p[3]][p[4]][p[5]] == 0) return 0;
        if(ful && cnt != sum) return 0;
    }
	return 1;//检查全都合格啦
}
vector<sol> ans;
void dfs(int pos)
{
    if(pos == 26)//都填完了
    {
        sol tmp;
        tmp.copy(a);
        ans.push_back(tmp);//记录答案
        return;
    }
    int x = order[pos][1], y = order[pos][2];
    if(pos == 1)//第一个数,得用他给的数
    {
        a[1][1] = k;
        dfs(2);
        a[1][1] = -1;//回溯
        return;
    }
    for(int i=0;i<=9;i++)//枚举搜索
    {
        a[x][y] = i;
        if(check(x, y))//填这个数可行
            dfs(pos + 1);//接着填
    }
    a[x][y] = -1;//回溯
    return;
}
int main()
{
    cope_prime();
    read(sum, k);
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)
            a[i][j] = -1;
    dfs(1);
    if(ans.empty()) puts("NONE");
    else
    {
        sort(ans.begin(), ans.end());
        for(auto i:ans)
            i.out();
    }
    return 0;
}

标签:10,return,1.3,int,质数,pos,10024,void
From: https://www.cnblogs.com/LittleN/p/17365041.html

相关文章

  • 筛质数
    筛质数:朴素筛法代码实现:#include<iostream>usingnamespacestd;constintN=1e5+5;intprime[N],vis[N],cnt;voidinit(intn){ for(inti=2;i<=n;i++){ if(!vis[i])prime[cnt++]=i; for(intj=i+i;j<=n;j+=i)vis[j]=1; }}intmain(){ intn; cin>&g......
  • 1.3 关于双指针的一些总结
    这篇内容主要是针对双指针的一些总结,方法比较巧妙,主要核心原理就是:有一个快指针fast、一个慢指针slow,slow指针主要作用就是存储真正的数组(也就是处理之后的结果),fast是辅助寻找元素,然后往slow里面放。典型例题:描述:给你一个数组nums 和一个值val,你需要原地移除所有数值等于......
  • Kubernetes 1.3 从入门到进阶 安装篇:minikube
    Kubernetes单机运行环境一直是一个没有得到重视的问题。现在我们有了minikube,一个用go语言开发的可以在本地运行kubernetes的利器,不过目前应该只是支持kubernetes1.3。如果你只有一台机器或者虚拟机又想试验一下Kubernetes的新的功能,或者作kubernetes上开发的本地环境,minikube可能......
  • Buildroot(2022.08-rc1)+busybox(1.35.0)启动流程
     关键词:busybox,inittab,syslogd,klogd,mdev,modprobe,watchdog,telnetd等等。 《busybox启动流程简单解析:从init到shelllogin》详细介绍了init对inittab的解析和执行。下面为buildroot(2022.08-rc1)的启动脚本:/etc/inittabsysinit->/bin/mount-tprocproc/proc......
  • 编译期生成随机质数
    Q1:为什么要随机质数A1:因为不随机可能会被hackQ2:为什么要编译期生成A2:编译期生成的话,编译器可以上取模常数优化Q3:你咋搞的A3:__TIME____TIMESTAMP__这两个宏。具体来说,每次编译后,生成的质数相同。重新编译后,生成的质数不同。#include<bits/stdc++.h>usingst......
  • 质数及其筛法
    筛法质数质数,又称素数。如果一个数\(a\in\N^+(a\neq1)\)的因子有且仅有\(1\)和它本身,则称数\(a\)为质数。普通筛法过程枚举\([2,n-1]\),如果\(n\)在这个范围内有因子,则\(n\)不是因数。因为\(n\)的因子成对出现,所以我们可以枚举\([2,\sqrt{n}]\)。Codeboolisprime(in......
  • 5.1.3 边界值法
    边界值的选择可以分为二值边界测试和三值边界测试。对于二值边界测试,应为每个边界选择两个输入,这些输入对应于边界上的值和等价划分边界外的增量距离;对于三值边界测试,应为每个边界选择三个输入,这些输入对应于边界上的值和等价划分边界的每一次的增量距离。增量距离应定义为对......
  • 101到200的质数
    质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。我们设定一个数为x,根据质数的定义判断x是否为质数,我们看它能否被2、3、4······、x-1整除,如果它不能被其中任何一个整数整除,则这个数就是质数。思路就是先判断一个数是不是质数,再去拓展......
  • 1.3数据库设计
    以下是一个衣服商城系统的数据库设计:用户表:存储用户信息,包括用户ID、用户名、密码、性别、联系电话、邮箱等。商品表:存储商品信息,包括商品ID、商品名称、商品价格、库存、品牌、型号、颜色、尺码、图片等。购物车表:存储购物车信息,包括用户ID、商品ID、数量、加入时间等。订单......
  • 输出100-200之间所有的质数
    输出100-200之间所有的质数<script>lettotal=0;//计数器for(leti=100;i<200;i++){letnum=true;for(letq=2;q<i;q++){if(i%q==0)/*余数为零,能被整除*/{n......