搜索之练习

(1)

从自然数1,2,…,n,从中任取r个数,输出所有组合。

输入格式

一行两个自然数n、r(1<n<21,1≤r≤n)。

输出格式

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

输入样例

5 3

输出样例

  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5

这道题很简单,一般思想都是初始化数字为最小 (记录各个位最小值)然后从个位开始改变不能变了将个位最小值+1 个位变为此最小值 十位+1

十位同理

当第一位递增超过r时结束

这里不用此一般方法,有搜索递归进行演示

#include<cstdio>
#include<cstring>
#include<iostream>
#include<iomanip>
#include<cstdlib>
using namespace std;
const double eps=1e-8;
const int MAX=0x3f3f3f3f;

int n;
int a[1000010];
int t;
void bfs(int begi,int n,int r,int sum)//当前取数范围  将要选取数字个数  已选取个数
{
    if(n<r)
        return ;
    if(sum>t)
    {
        for(int i=0;i<t;i++)
        {
            cout<<setw(3)<<a[i];
        }
        cout<<endl;
    }
    else
    {
        for(int i=begi;i<=n;i++)
        {
            a[sum-1]=i;
            bfs(i+1,n,r,sum+1);
        }
    }

}
int main()
{
    int n,r;
    cin>>n>>r;
    t=r;
    bfs(1,n,r,1);

    return 0;
}

(2)

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
当n=7共14种拆分方法:参考输出样例

输入格式

输入有多组数据,每组数据一行,每行给出一个正整数n(2<=n<=20)。

输出格式

对于每组数据输出若干行,参考样例。
相邻两组样例之间输出一个空行。

输入样例 

2
7

输出样例 

2=1+1

7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int MAX=0x3f3f3f3f;

int a[1000010]= {1};
int t;

int n,r;
void dfs(int sum,int step)
{
    for(int i=a[step-1]; i<=sum; i++)
    {
        if(i<n)//保证按字典序
        {
            a[step]=i;
            sum-=i;
            //以上两步连起来看   将末尾添加一个i

            //用剩余数减去此i
            //为0继续打印就好
            //为负不满足字典序被if  排除  返回上一层
            //余数恢复
            //i++  保证字典序
            //周而复始
            if(sum==0)
            {
                int f=0;
                cout<<t<<"=";
                for(int i=1;i<=step;i++)
                {
                    if(f)
                        cout<<"+";
                    cout<<a[i];
                    f=1;
                }
                cout<<endl;
            }
            else
                dfs(sum,step+1);
            sum+=i;//恢复
        }
    }
}
int main()
{
    cin>>n;
     t=n;
    dfs(n,1);
    //cout<<endl;
    while(cin>>n)
    {
        t=n;
        cout<<endl;
        dfs(n,1);
    }
    return 0;
}

 这道题不算简单细节比较多

结合输出来说下原理(虽然好像已近在代码里面了)

本质上就是凑余数

打印条件是余数为0

期间控制字典序(i的递增,i的判断)

由于一开始num余数为最大

且初始化为1

可以得到初始为全1的结果

打印后递归返回  余数复位

位数被动-1  (因为从step+1的递归退出)

将该数赋值为i继续试减

调试代码:

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int MAX=0x3f3f3f3f;

int a[1000010]= {1};
int t;

int n,r;
void dfs(int sum,int step)
{
    for(int i=a[step-1]; i<=sum; i++)
    {
        if(i<n)//保证按字典序
        {
            a[step]=i;
            sum-=i;
            if(sum==0)
            {
                cout<<"  if 进入    ";
                int f=0;
                cout<<t<<"=";
                for(int i=1; i<=step; i++)
                {
                    if(f)
                        cout<<"+";
                    cout<<a[i];
                    f=1;
                }
                cout<<endl;
                
            }
            else
            {
                cout<<"else 进入    ";
                int f=0;
                cout<<t<<"=";
                for(int i=1; i<=step; i++)
                {
                    if(f)
                        cout<<"+";
                    cout<<a[i];
                    f=1;
                }
                cout<<endl;
                
                dfs(sum,step+1);
            }

            sum+=i;//恢复
        }
    }
}
int main()
{
    cin>>n;
    t=n;
    dfs(n,1);
    //cout<<endl;
    while(cin>>n)
    {
        t=n;
        cout<<endl;
        dfs(n,1);
    }
    return 0;
}

一目了然 

(3)

输入正整数n,把整数1,2,...,n组成一个环,使得相邻两个整数之和均为素数。输出时,从整数1开始逆时针排列。同一个环恰好输出一次。

输入格式

输入存在多组测试数据,每组测试数据输入一个正整数n(n<=16)

输出格式

输出的时候第i组测试数据输出Case i:
然后输出若干行,每一行n个数字表示素数环,由于每个环只输出一次,也就是第一个数字永远是1
按照第二位从小到大,第二位相同第三位从小到大,以此类推输出所有方案
相邻两组测试数据之间输出一个空行

输入样例 

6
8

输出样例 

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

这道题太难优化了,这是1s能完成的程序????????????

 我整个计算机都在响

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-8;
const int MAX=0x3f3f3f3f;

int n,r;

int a[110];
int used[20];
int sum=1;
int prime[20]={0,2,3,5,7,11,13,17,19,23,29,31};
//从prime[1]-[11]
int ispre(int x)
{
    /*
    for(int k=2; k<=sqrt(x); k++)
        if(x%k==0)
        {
            // cout<<"不是素数"<<endl;
            return 0;
        }
    // cout<<"是素数"<<endl;
    return 1;
    */
    //int f=0;
    for(int k=1;k<=11;k++)
    {
        //速度应该还会慢
        if(x==prime[k])
            return 1;
    }
    return 0;
}

void bfs(int x)
{
    //cout<<"进入"<<endl;
    for(int i=2; i<=n; i++)
    {
        if((!used[i])&&ispre(a[x-1]+i))
        {
            used[i]=1;
            a[x]=i;

            //cout<<"筛选成功一个"<<endl;
            //cout<<"当前x  n  ="<<x<<" "<<n<<endl;
            //cout<<"最后一个值"<<a[n]<<endl;
            if((x==n)&&ispre(1+a[n]))
            {
                //打印
                //cout<<"进入打印"<<endl;

                int f=0;
                for(int j=1; j<=n; j++)
                {
                    if(f)
                        cout<<" ";
                    cout<<a[j];
                    f=1;
                }
                cout<<endl;

            }
            else
            {
                // cout<<"进入下一层"<<endl;
                bfs(x+1);
            }
            used[i]=0;
        }
        //cout<<"下一个"<<endl;

    }
}
int main()
{
    /*for(int i=2;i<=100;i++)
    {
        if(ispre(i))
            cout<<i<<" ";
    }*/
    //system("pause");
   // initialize();
    cin>>n;
    int sum=1;
    memset(a,0,sizeof(a));
    memset(used,0,sizeof(used));
    a[1]=1;
    used[1]=1;

    cout<<"Case "<<sum<<":"<<endl;
    sum++;
    bfs(2);
    while(cin>>n)
    {
        cout<<endl;
        cout<<"Case "<<sum<<":"<<endl;
        sum++;

        int sum=1;
        memset(a,0,sizeof(a));
        memset(used,0,sizeof(used));
        a[1]=1;
        used[1]=1;
        bfs(2);

    }
    return 0;
}

先放这里吧,以后再看看

注意在搜索题中临时变量一般用局部变量,

这样可以避免一些改变计数变量的小问题

22.02.16更新

尝试了一下埃氏筛,貌似也不行

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-8;
const int MAX=0x3f3f3f3f;

int n,r;

int a[110];
int used[20];
int sum=1;
int prime[32]={0};
//从prime[1]-[11]
void initialize()//埃氏筛
{
    fill(prime,prime+32,1);
    //memset(prime,1,sizeof(a));
    //for(int i=3;i<=32;i++)
      //  prime[i]=1;
    prime[0]=prime[1]=0;
    for(int i=2;i<=32;i++)
    {
        if(prime[i])
        {
            for(int j=2*i;j<=32;j+=i)
                prime[j]=0;
        }
    }
}
/*
int ispre(int x)//普通素数判断
{
    /*
    for(int k=2; k<=sqrt(x); k++)
        if(x%k==0)
        {
            // cout<<"不是素数"<<endl;
            return 0;
        }
    // cout<<"是素数"<<endl;
    return 1;

    //int f=0;
    for(int k=1;k<=11;k++)
    {
        //速度应该还会慢
        if(x==prime[k])
            return 1;
    }
    return 0;
*/

void bfs(int x)
{
    //cout<<"进入"<<endl;
    for(int i=2; i<=n; i++)
    {
        if((!used[i])&&prime[a[x-1]+i])
        {
            used[i]=1;
            a[x]=i;

            //cout<<"筛选成功一个"<<endl;
            //cout<<"当前x  n  ="<<x<<" "<<n<<endl;
            //cout<<"最后一个值"<<a[n]<<endl;
           // if((x==n)&&ispre(1+a[n]))
           if((x==n)&&prime[1+a[n]])
            {
                //打印
                //cout<<"进入打印"<<endl;

                int f=0;
                for(int j=1; j<=n; j++)
                {
                    if(f)
                        cout<<" ";
                    cout<<a[j];
                    f=1;
                }
                cout<<endl;

            }
            else
            {
                // cout<<"进入下一层"<<endl;
                bfs(x+1);
            }
            used[i]=0;
        }
        //cout<<"下一个"<<endl;

    }
}
int main()
{
    /*for(int i=2;i<=100;i++)
    {
        if(ispre(i))
            cout<<i<<" ";
    }*/
    //system("pause");
    initialize();
    /*
    for(int i=1;i<=32;i++)
    {
        cout<<prime[i]<<" ";
    }
    cout<<endl;
    */
    cin>>n;
    int sum=1;
    memset(a,0,sizeof(a));
    memset(used,0,sizeof(used));
    a[1]=1;
    used[1]=1;

    cout<<"Case "<<sum<<":"<<endl;
    sum++;
    bfs(2);
    while(cin>>n)
    {
        cout<<endl;
        cout<<"Case "<<sum<<":"<<endl;
        sum++;

        int sum=1;
        memset(a,0,sizeof(a));
        memset(used,0,sizeof(used));
        a[1]=1;
        used[1]=1;
        bfs(2);

    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值