洛谷 P2404 自然数的拆分问题C语言

文章目录[x]
  1. 1:2.深度优先搜索
  2. 2:3.解题
  3. 2.1:正解(升序+不去重)
  4. 2.2:(去重+升序)

@TOC


# 前言


之前看了好多文章都说学习编程时写博客的重要,但一直没有尝试,今天在这发第一篇来开个头吧。
这篇是我刷洛谷时的一道题,希望大家多多指教QAQ


刚刚大一,第一篇记录如下?

一、题目

在这里插入图片描述


# 二、解题思路
## 1.考察方向
这道题题目理解很简单,自然数拆分,但要是用单纯循环却很难解决。因此我们想到dfs深搜(深度优先搜索)进行解题。这道题是一道很典型的dfs入门题型,由此可以引发许多变式。


2.深度优先搜索

简介

深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。

这么多不如一张图:
在这里插入图片描述
我的dfs模版(深搜回溯)
search(i){
for(所有算子){
if(条件成立) 输出;
else search(i+1);
回溯
}
}
}

3.解题

正解(升序+不去重)

#include<stdio.h>

int a[100];
int n;
void print(int j)//输出数据
{
    for(int i=0;i<=j;i++)
       {
           if(i==0) printf("%d",a[i]);
   else  printf("+%d",a[i]);
       }
       printf("\n");
}
void search(int sum,int j ,int s)//dfs深搜
{
  for(int i=s;i<=n;i++)//从第s个开始搜,i=s保证是升序
{sum+=i;  
      if(sum>n) return;
      if(sum==n) {a[j]=i;print(j);return;}
      a[j]=i;

      search(sum,j+1,i);
        sum-=i;//回溯
  }
}
int main()
{
    while(scanf("%d",&n))//连续输入
    {
        search(0,0,1);
    }
    return 0;
}

运行截图
在这里插入图片描述
我们这题中运用回溯的技巧,即sum -= i,在搜索中,不要过多注意细枝末节,宏观分析函数总体功能。

什么在控制升序?
我们可以从 for(int i=s;i<=n;i++) 看出,我们在“播撒”算子的时候,起始时在控制是否升序,倘若我用
for(int i=1;i<=n;i++)即不要s变量 ,这样的结果为
在这里插入图片描述
我们可以画图理解。

(去重+升序)

#include<stdio.h>
int book[100];
int a[100];
int n;
void print(int j)
{
    for(int i=0;i<=j;i++)
       {
           if(i==0) printf("%d",a[i]);
   else  printf("+%d",a[i]);
       }
       printf("\n");
}
void search(int sum,int j,int s)
{
  for(int i=s;i<=n;i++)
  if(book[i]==0){sum+=i;
      if(sum>n) return;
      if(sum==n) {a[j]=i;print(j);return;}
      a[j]=i;
      book[i]=1;
      search(sum,j+1,i);

      book[i]=0;
        sum-=i;
  }
}
int main()
{
    while(scanf("%d",&n))
    {
        search(0,0,1);
    }
    return 0;
}

此结果为
在这里插入图片描述
在这里,我们又更进一步运用回溯的作用,用book【】建立判断数组来判断每个数是否取过,若取过,则在此记录数组a【】处标1,直到最后走到枝节末端向上时return一级回溯为0一次,以此类推,即可知数是否取过。

总结

在此题中不仅运用深搜,而且还有回溯的小技巧,对于像我一样的新手是一个很好的练习。
望多多指教

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像