【搜索练习】【一】
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; }
1542: n个数中取m个数从小到大排列
题目描述
n个数中取m个数从小到大排列,详见样例。
输入
输出
样例输入
3 2
样例输出
1 2
1 3
2 3
当你要输出全排列的时候,你的cur是到N+1的时候结束
所以就不需要考虑排序的问题啦
#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; }
题目描述
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; }
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; }
#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; }
1545: 字符串回文判定
题目描述
读入若干行字符串,判断它是否构成回文。是的话输出"yes",不是的话输出"no"。
输入的字符串以"end"结束。
输入
输出
样例输入
abcbac
abccba
abcba
end
样例输出
no
yes
yes
1631: 字符序列
题目描述
输入
输入一个整数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; }
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; }
一些优化的可行:
①
所以直接for一遍判断就好了,比搜索快
也就是只要搜到长度的一半就好了
后面直接从前面翻过来
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; }
早期版
#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; }
一些奇怪的想法//未修正
#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; }
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; }