【搜索练习】【一】

 

n的全排列

题目描述

输入一个整数n,输出的n的全排列。

输入

 

输出

 

样例输入

3

样例输出

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;
int n;
int visi[1005],a[1005];
void print(int x) 
{ 
    for(int i=1;i<x;++i)printf("%d ",a[i]); 
    printf("%d",a[x]); 
    puts("");
} 
void dfs(int cur) 
{ 
    if(cur==n+1)print(cur-1); 
    else for(int i=1;i<=n;i++) 
    { 
        if(!visi[i]) 
        { 
            a[cur]=i; 
            visi[i]=1; 
            dfs(cur+1); 
            visi[i]=0; 
        } 
    } 
} 

int main()
{
    cin>>n;
    dfs(1);
    puts("");
    return 0;
}
9018 1541

 

 

 


 

1542: n个数中取m个数从小到大排列

题目描述

n个数中取m个数从小到大排列,详见样例。

输入

 

输出

 

样例输入

3 2

样例输出

1 2
1 3
2 3


当你要输出全排列的时候,你的cur是到N+1的时候结束

 那么cur记录的是当前答案长度是多少 
 那么你要取M个 答案长度就是M
他是按照字典序输出的 
你搜索的时候找这个位置上的数是多少肯定是for1到n
那么先输出的肯定是字典序最小的

所以就不需要考虑排序的问题啦

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
int n,m;
bool vis[55];
int a[55];
void print(int x)
{
    for(int i=1;i<x;++i)cout<<a[i]<<" ";
    cout<<a[x];
    puts("");
}
void dfs(int cur)
{
    if(cur==m+1)print(cur-1);
    for(int i=a[cur-1]+1;i<=n;++i) 
//防止重复搜索比当前数小的数 失去排序作用
    {
        if(!vis[i])
        {
            a[cur]=i;
            vis[i]=1;
            dfs(cur+1);
            vis[i]=0;
        }
    }
}
int main()
{
    cin>>n>>m;
    dfs(1);
    puts("");    
    return 0;
}
View Code

 


 

 

 

题目描述

n个数中取m个数和为偶数的有几种,按字典序输出,详见样例。

输入

 

输出

 

样例输入

4 2

样例输出

1+3=4
2+4=6

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
bool vis[105];
int a[105];
int n,m;

void print(int x)
{
    int sum=0;
    for(int i=1;i<=x;++i)sum+=a[i];
    if(sum%2!=0)return;//特判和为偶数时输出
    else
    {
        for(int i=1;i<x;++i)printf("%d+",a[i]);
        printf("%d=",a[x]);
        cout<<sum;
        puts("");
    }
}

void dfs(int cur)
{
    if(cur==m+1)print(cur-1);
    else 
    {
        for(int i=a[cur-1]+1;i<=n;++i)
        {
            if(!vis[i])
            {
                a[cur]=i;
                vis[i]=1;
                dfs(cur+1);
                vis[i]=0;
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    dfs(1);
    puts("");    
    return 0;
}
View Code

 

 


1544: 整数分解

题目描述

输入一个整数n,按照字典序从小到大输出它的分解情况

输入

 

输出

 

样例输入

4

样例输出

1+1+1+1=4
1+1+2=4
1+3=4
2+2=4
4=4

他要字典序从小到大 所以本质上我们还是做一个跟全排列类似的过程 只是dfs要记录的东西比较多
用cur记录当前答案的长度 sum记录当前和 limit记的是当前位置上的数字大小
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
int n,sum;
int a[105];
void print(int x,int sum)
{
    if(sum>n)return;
    else
    {
        for(int i=1;i<x;++i)printf("%d+",a[i]);
        printf("%d=%d",a[x],sum);
        puts("");
    }
}
void dfs(int cur,int sum)
{
    if(sum>=n)print(cur-1,sum);
    else
    {
        for(int i=a[cur-1];i<=n;++i)
        {
            a[cur]=i;
            dfs(cur+1,sum+i);
        }
    }
}
int main()
{
    cin>>n;
    a[0]=1;
    dfs(1,0);
    puts("");    
    return 0;
}
a[cur-1]版记录
#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
  
using namespace std; 
int n,sum,limit; 
int a[105]; 
void print(int x,int sum) 
{ 
    if(sum>n)return; 
    else
    { 
        for(int i=1;i<x;++i)printf("%d+",a[i]); 
        printf("%d=%d",a[x],sum); 
        puts(""); 
    } 
} 
void dfs(int cur,int sum,int limit) 
{ 
    if(sum>=n)print(cur-1,sum); 
    else
    { 
        for(int i=limit;i<=n;++i) 
        { 
            a[cur]=i; 
            dfs(cur+1,sum+i,i); 
        } 
    } 
} 
int main() 
{ 
    cin>>n; 
    a[0]=1; 
    dfs(1,0,1); 
    puts("");    
    return 0; 
} 
limit版

 




1545: 字符串回文判定

题目描述

读入若干行字符串,判断它是否构成回文。是的话输出"yes",不是的话输出"no"。

输入的字符串以"end"结束。

 

输入

 

输出

 

样例输入

abcbac
abccba
abcba
end

样例输出

no
yes
yes











 

1631: 字符序列

题目描述

从三个元素的集合[A,B,C]中选取元素生成一个N个字符组成的序列,使得没有长度为2的相邻子序列相同。例:N = 5时ABCBA是合格的,而序列ABCBC与ABABC是不合格的,因为其中子序列BC,AB是相同的。

输入

输入一个整数n,n<=13

输出

求出满足条件的N个字符的所有序列总数

样例输入

4

样例输出

72


找出所有的长度为n的序列 然后判断是否符合 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
int n,ans=0;
int a[20];
void check()
{
    for(int i=1;i<=n-2;++i)
    {
        if(a[i]==a[i+2]&&a[i+1]==a[i+3])return;
    }
    ++ans;
}
void dfs(int now)
{
    if(now>n)
    {
        check();return;
    }
    else
    {        
        for(int i=1;i<=3;++i)
        {
            a[now]=i;
            dfs(now+1);
            a[now]=0;
        }
    }
}
int main()
{
    cin>>n;
    dfs(1);
    cout<<ans;
    puts("");    
    return 0;
}
View Code
 



 

1674: 回文素数

题目描述

一个数如果从左往右读和从右往左读数字是相同的,则称这个数是回文数,如121,1221,15651都是回文数。给定位数n,找出所有既是回文数又是素数的n位十进制数。(注:不考虑超过整型数范围的情况)。

输入

位数n,其中1<=n<=5。

输出

第一行输出满足条件的素数个数。
第二行按照从小到大的顺序输出所有满足条件的素数,两个数之间用一个空格区分。

样例输入

1

样例输出

4
2 3 5 7



考虑到首位不能是0 所以首尾都不能是0
有(fei)点(chang)蠢的想法是 我可以强行暴力的for 0-n 然后特判一下 i=0的时候now=1||n不能搞成0 

优化一下判断素数的方式

//别忘了要判断 ①是不是素数 ②是不是构成回文

//变量名别打错 别问我怎么知道的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int n,ans=0;
int a[20];
int owo[5005]={};
void check()
{
    int res=0,shi=1;
    for(int i=n;i>=1;--i)
    {
        res+=a[i]*shi;
        shi*=10;
    }
    if(res==1)return;
    int pd=n/2;
    for(int i=1;i<=pd;++i)
    {
        if(a[i]!=a[n-i+1])return;
    }
    pd=sqrt(res);
    for(int i=2;i<=pd;++i)
    {
        if(res%i==0)return;
    }
    
    ++ans;
    owo[ans]=res;
//    cout<<res<<" ";
}
void dfs(int now)
{
    if(now>n)
    {
        check();return;
    }
    else
    {    
        if(now==1)    
        for(int i=1;i<=9;++i)
        {
            {
                a[now]=i;
                dfs(now+1);
                a[now]=0;
            }        
        }
        else    
        for(int i=0;i<=9;++i)
        {
            {
                a[now]=i;
                dfs(now+1);
                a[now]=0;
            }        
        }
    }
}
int main()
{
    cin>>n;
    dfs(1);    
    cout<<ans;
    puts("");
    int pos=1;
    while(owo[pos]!=0)
    {
        printf("%d ",owo[pos]);
        ++pos;
    }
    return 0;
}
很慢的乱七八糟版本

 一些优化的可行:

因为n只有5
所以直接for一遍判断就好了,比搜索快
 你可以在搜索的时候找回文
也就是只要搜到长度的一半就好了
后面直接从前面翻过来 
 对于长度是奇数的要枚举一半+1
或者 对奇数枚举一半 中间位0-9填



 

1079: 选数

题目描述

  已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22  3+7+19=29  7+12+19=38  3+12+19=34。
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29)。

输入

键盘输入,格式为:
n , k (1<=n<=20,k<n)
x1,x2,…,xn (1<=xi<=5000000)

输出

 屏幕输出,格式为:
一个整数(满足条件的种数)。

样例输入

4 3
3 7 12 19

样例输出

1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,a[25],ans=0;
int zs(int n){
    int i;
    if(n==0||n==1)return 0;
    if(n==2)return 1;
    for(i=2;i*i<=n;i++)if(n%i==0)return 0;
    return 1;
}
void dfs(int k,int i,int s){
    if(!k){ans+=zs(s);return;}
    for(i;i<=n;i++)dfs(k-1,i+1,s+a[i]);
}
int main()
{
    memset(a,0,sizeof(a));
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dfs(k,1,0);
    printf("%d",ans);
    return 0;
}
View Code

 早期版

 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
int n,k;
int a[30],choose[30];
bool vis[30];
int ans=0;
void zs(int s)
{
    if(s==1)return;
    int pd=sqrt(s);
    for(int i=2;i<=pd;++i)if(s%i==0)return;
    ++ans;
}
void dfs(int cur,int s,int limit)
{
    if(cur==k+1)zs(s);
    else for(int i=limit;i<=n;++i) 
    {
        if(!vis[i]) 
        {
//            choose[cur]=a[i];
            vis[i]=1; 
            dfs(cur+1,s+a[i],i); 
//            choose[cur]=0;
            vis[i]=0;
        }
    }
}
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    dfs(1,0,1);
    cout<<ans;
    puts("");
    return 0;
}
limit

 

一些奇怪的想法//未修正

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
int n,k;
int a[30],choose[30];
bool vis[30];
int ans=0;
int zs(int s)
{
    if(s==1)return 0;
    int pd=sqrt(s);
    for(int i=2;i<=pd;++i)if(s%i==0)return 0;
    return 1;
}
void dfs(int now,int cur,int sum)

{
//    if(cur>n)return;
    if(cur<=n&&now<=0)if(zs(sum)){
        ++ans;return;
    }
    dfs(now-1,cur+1,sum+a[cur]);
    dfs(now,cur+1,sum);
}

int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;++i)cin>>a[i];
    dfs(k,1,0);
    cout<<ans;
    puts("");
    return 0;
}
View Code

 

clx资磁
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>

using namespace std;

int n,m,a[21],sum[200000],tmp,vis[21],t,ans;

/*
 *    n个数中取出m个,n个数分别是a[1-20],
 *    从20个数中选出若干加和,可能的和的数目不会超过200000个,将所有找到的和存在sum内,
 *    tmp用于dfs储存当前的和,
 *    vis用于dfs记录哪些数字已经被选,
 *    t用于记录sum中一共有多少可能的和,
 *    ans用于输出答案.
*/

bool isprime(int x){    //判断质数
    if(x<=1) return false;
    if(x==2) return true;
    int lim=sqrt(x);
    for(int i=2;i<=lim;i++){
        if(x%i==0) return false;
    }
    return true;
}

int dfs(int p,int q){    //深搜,当前挑选m个数字中的第p个,上一个挑选的数字在n个数字中排第q-1位
    if(p==m+1){                    //到终点,检查,记录
        if(isprime(tmp)){
            sum[++t]=tmp;
        }
        return 0;
    }
    for(int i=q;i<=n;i++){        //只挑在上一个数字之后的数字,也就是比上一个数字大的数字
        if(!vis[i]){
            vis[i]=1;
            tmp+=a[i];
            dfs(p+1,i+1);
            tmp-=a[i];
            vis[i]=0;
        }
    }
    return 0;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a+1,a+n+1);            //排序,使得后dfs发现的和一定不比之前发现的来得小,免去排序
    dfs(1,1);
    for(int i=1;i<=t;i++){
        if(sum[i]!=sum[i-1])
            ans++;                //去重统计
    }
    cout<<ans<<endl;
}
View Code

 






posted @ 2017-06-23 13:50  pandaB  阅读(511)  评论(0编辑  收藏  举报