惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

通过 1Panel 部署 Astro 站点到服务器——公开仓库篇 | Frederick's Blog 用违背道德的行为,抨击合理的事实? | Frederick's Blog 博客三周年:缝缝补补的过去 | Frederick's Blog 观纪录片《苏轼》(下) | Frederick's Blog 观纪录片《苏轼》(上) | Frederick's Blog 《南京照相馆》:在战争的废墟上,显影人性的底片 | Frederick's Blog 观影 |《南京照相馆》 | Frederick's Blog Windows 7 终端开启右键粘贴 | Frederick's Blog 算法竞赛:为什么要写总结? | Frederick's Blog OI 赛制的实用工具 | Frederick's Blog 算法竞赛:拥有一个顺手的 IDE · Windows 篇 | Frederick's Blog 博客两周年,换框架? | Frederick's Blog 数据备份太重要了 | Frederick's Blog WeChat(微信国际服务)与微信(国内服务)的《隐私协议》的天壤之别(其中最让人愤怒的) | Frederick's Blog 题解 | 字符串反转 | Frederick's Blog Codesign:高效设计的秘诀 | Frederick's Blog Github:设置两步验证(2FA) | Frederick's Blog
题解 | 素数环 | Frederick's Blog
2024-06-27 · via

前言:

在学校信息队训练的时候布置了这一道题目,但是只求一次。然后上洛谷发现UVA题库求多次,因为方法一样只需嵌套,所以就做了一下(没有账号提交不了=m=)

信息队原题

原题图片

1.题目分析

题目要求将整数 $1$ 到 $n$ 组成一个环,使得相邻的两个整数之和均为素数。

我们需要找到所有满足条件的素数环,并按照要求输出。

由于 $n$ 的值不超过 $16$ ,我们可以使用回溯法来尝试所有可能的组合。

2.做题思路

判断当前取出的数字是否和前一个组成素数,这一步可以用一个 check函数来解决(num的值为两个数字的和)

bool check(int num)
{
    if (num < 2)
    {
        return false;
    }
    for (int i = 2; i * i <= num; i++)
    {
        if (num % i == 0)
        {
            return false;
        }
    }
    return true;
}

再创建一个 back 函数用于回溯:

void back(int cur)  // cur 表示当前位置
{
    if (cur == n)  // 检查最后一个数字和第一个数字之和是否为素数
    {
        if (check(arr[0] + arr[n - 1]))
        {
            for (int i = 0; i < n; i++)
            {
                cout << arr[i] << " ";
            }
            cout << endl;
        }
        return;
    }
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i] && check(arr[cur - 1] + i))
        {
            vis[i] = true;
            arr[cur] = i;
            back(cur + 1);
            vis[i] = false;
        }
    }
}

在back函数中,首先判断是否已经放置了 $n$ 个数字。如果是,则检查最后一个数字和第一个数字之和是否为素数。如果是素数,则输出这个环。

然后,从数字 $2$ 到 $n$ 依次尝试将每个数字放在环的下一个位置。如果该数字没有被使用过,并且与前一个数字之和为素数,就将其放在环的下一个位置,并标记为已使用,然后继续递归放置下一个数字。

如果放置过程中出现相邻两个数字之和不是素数的情况,就回溯到上一个数字,重新选择下一个数字。

最后是 main 函数,在main函数中,读取输入的 $n$ ,初始化标记数组和环的第一个数字,然后调用 back 函数从数字 $1$ 开始回溯。

3.复杂度计算

时间复杂度:由于需要尝试所有可能的组合,时间复杂度为 $O(n!)$ ,其中 n 是输入的数字个数

空间复杂度:主要是使用了标记数组和环数组,空间复杂度为 $O(n)$

4.完整代码

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

const int MAXN = 16;
int n;
int arr[MAXN];
bool vis[MAXN];

// 判断一个数是否为素数
bool check(int num)
{
    if (num < 2)
    {
        return false;
    }
    for (int i = 2; i * i <= num; i++)
    {
        if (num % i == 0)
        {
            return false;
        }
    }
    return true;
}

// 回溯
// cur表示当前位置
void back(int cur)
{
    if (cur == n)  // 检查最后一个数字和第一个数字之和是否为素数
    {
        if (check(arr[0] + arr[n - 1]))
        {
            for (int i = 0; i < n; i++)
            {
                cout << arr[i] << " ";
            }
            cout << endl;
        }
        return;
    }
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i] && check(arr[cur - 1] + i))
        {
            vis[i] = true;
            arr[cur] = i;
            back(cur + 1);
            vis[i] = false;
        }
    }
}

int main()
{
    cin >> n;
    memset(vis, false, sizeof(vis));
    vis[1] = true;
    arr[0] = 1;
    back(1);
    return 0;
}

洛谷UVA题库版

前面不是说了吗,UVA也有一个版本,是给出多个值,这里给出题目:

洛谷UVA题目

样例组

INPUT #1

`6
8
`

OUTPUT #1

`Case 1:
1 4 3 2 5 6
1 6 5 2 3 4

Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
`

其实就是多次输入,用 while 循环解决,终止条件为 n == EOF,所以可以这样写。

`while(n != EOF)
    {
        cin >> n;
        memset(vis, false, sizeof(vis));
        vis[1] = true;
        arr[0] = 1;
        back(1);
    }
`

现在是把刚才信息队版本的输入和操作套在了循环里,这就完了,是不是很简单?

其实没完,题目中说要在第 i 行添加上 Case i ,所以再搞一个变量就完事了!

`int idx = 1;  // 此处 idx 变量即 i 变量
while(n != EOF)
{
    cin >> n;
    cout << "Case " << idx++ << ":" << endl;  // 输出 Case i 因为 "++" 在后是先赋值再运算,所以可以这么写,就相当于先输出 idx 当前值再加
    memset(vis, false, sizeof(vis));
    vis[1] = true;
    arr[0] = 1;
    back(1);
}
`

最终代码

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

const int MAXN = 16;
int n;
int arr[MAXN];
bool vis[MAXN];

// 判断一个数是否为素数
bool check(int num)
{
    if (num < 2)
    {
        return false;
    }
    for (int i = 2; i * i <= num; i++)
    {
        if (num % i == 0)
        {
            return false;
        }
    }
    return true;
}

// 回溯
// cur表示当前位置
void back(int cur)
{
    if (cur == n)  // 检查最后一个数字和第一个数字之和是否为素数
    {
        if (check(arr[0] + arr[n - 1]))
        {
            for (int i = 0; i < n; i++)
            {
                cout << arr[i] << " ";
            }
            cout << endl;
        }
        return;
    }
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i] && check(arr[cur - 1] + i))
        {
            vis[i] = true;
            arr[cur] = i;
            back(cur + 1);
            vis[i] = false;
        }
    }
}

int main()
{
    int idx = 1;
    while(n != EOF)
    {
        cin >> n;
        cout << "Case " << idx++ << ":" << endl;
        memset(vis, false, sizeof(vis));
        vis[1] = true;
        arr[0] = 1;
        back(1);
    }
    return 0;
}
`

写在最后

到这里本片题解就结束了,UVA版本没提交,前面说了注册不起账号,但是代码应该没问题,有问题评论告知,谢谢支持🙏