【C语言 每日一题】SOJ 1002 Anti-prime Sequences

题意

对于数列 a1,a2,...,an ,如果满足所有相邻的两个数之和都为合数(非质数的话),则该数列被称作 anti-prime sequences。再对这个定义进行扩展,如果所有相邻的 d 个数之和为合数,则该数列就是 danti-prime sequences

对给定正整数 n、m,满足 1 <= n < m <= 1000;给定正整数 d,满足 2 <= d <= 10。求 n,n+1,...,m 是否可以组成danti-prime sequences;若可以的话,给出按字典序的第一个 danti-prime sequences

题解

这道题楼主卡了很久,一直没想通该怎么做。在网上搜相关的解答,都在说 DFS(也就是深度优先搜索),我知道这个算法啊,哪里用得上???只能说萌新一脸懵逼。不过后来我仔细地看了下各路大神的代码,才 AC 了,原来真的是 DFS!我下面描述下思路:

  1. 从数列 source n,n+1,...,m )中取出一个未被标记的数,放入(push)目标数列 result,并标记这个数;
  2. 判断当前数列是否为 danti-prime sequences
  3. 如果是,判断 result 数列元素数量是否达到 m - n + 1,若达到,结束逻辑,否则回到第 1 步;
  4. 如果不是,则从 result 移出该数,取消该数的标记,再从 source 中取下一个未被标记的数放入(push) result。回到第 2 步。
  5. result 数列元素数量达到 m - n + 1 时,退出逻辑。

代码

#include <stdio.h>
#include <string.h>

// 素数表上限
#define PRIME_LIMIT 10000
// anti-prime sequences 数组上限
#define LIMIT 1005

typedef enum { FALSE = 0, TRUE = 1 } boolean;

int n, m, d;                  // 输入参数
boolean found;                // 是否结束
boolean primes[PRIME_LIMIT];  // 素数表
boolean visited[LIMIT];       // 已访问列表
int result[LIMIT];            // 返回数组

void dfs(int degree);
boolean passWith(int num);

int main() {
  int i, j;

  memset(primes, TRUE, sizeof(primes));

  primes[1] = FALSE;
  for (i = 2; i < PRIME_LIMIT; ++i)
    if (primes[i]) {
      for (j = i * 2; j < PRIME_LIMIT; j += i)
        primes[j] = FALSE;
    }

  while (scanf("%d%d%d", &n, &m, &d), n != 0 && m != 0 && d != 0) {

    found = FALSE;
    memset(visited, FALSE, sizeof(visited));

    dfs(0); // 开始搜索
    if (found) {
      for (i = 0; i < m - n; ++i)
        printf("%d,", result[i]);
      printf("%d\n", result[i]);
    } else
      printf("No anti-prime sequence exists.\n");

  }

}

/**
 * DFS,当找到时停止搜索
 */
void dfs(int current) {
  int i;

  // 如果已经找到
  if (found)
    return;
  // 如果当前数列不是 anti-prime sequence
  if (!passWith(current))
    return;
  // 如果当前已经满了
  if (current == m - n + 1) {
    found = TRUE;
    return;
  }

  for (i = n; i <= m && !found; ++i) {
    if (visited[i]) continue; // 用过的就跳过(回溯的时候有用)

    result[current] = i;      // push 一个数进结果数列
    visited[i] = TRUE;
    dfs(current + 1);         // 往下搜索
    visited[i] = FALSE;       // 搜索完去掉标记
  }
}


/**
 * 判断 end 前 2,3,...,d 个数是否和为合数。
 * _ _ end - d _ ... _ end
 */
boolean passWith(int end) {

  // d >= 2
  if (end <= 1) return TRUE;

  int sum;
  int i, j;

  sum = result[end - 1];
  for (i = end - 2; i >= end - d; --i) {
    sum += result[i];
    if (primes[sum]) return FALSE;
  }

  return TRUE;
}

  1. 这题好难 T T
  2. 逗号表达式的值为最后一个子表达式
  3. Sicily 提交代码不支持注释…药丸
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值