一维——穷举数
【codevs1294】全排列
Luogu-1706
题目描述 Description
给出一个n, 请输出n的所有全排列
输入描述 Input Description
读入仅一个整数n (1<=n<=10)
输出描述 Output Description
一共n!行,每行n个用空格隔开的数,表示n的一个全排列。并且按全排列的字典序输出。
样例输入 Sample Input
3
样例输出 Sample Output
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
方法一:
#include<iostream>
#include<cstdio>
using namespace std;
int n,pd[21],used[21];
void print()
{
int i;
for(i=1;i<=n;++i)
printf("%d ",used[i]);
printf("\n");
}
void dfs(int k)
{
int i;
if(k==n){print();return;}
for(i=1;i<=n;i++)
if(!pd[i]){
pd[i]=1;used[k+1]=i;
dfs(k+1);
pd[i]=0;
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
方法二:
#include<cstdio>
#include<iostream>
using namespace std;
int a[10],used[10];
int n;
void print()
{
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
cout<<endl;
}
void dfs(int k)
{
for(int i=1;i<=n;i++)
if(used[i]==0)
{used[i]=1;a[k]=i;dfs(k+1);used[i]=0;}
if(k==n)print();
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
【codevs4069】2194 N皇后
题目描述 Description
检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
列号
| 1 | 2 | 3 | 4 | 5 | 6 |
1 |
| O |
|
|
|
|
2 |
|
|
| O |
|
|
3 |
|
|
|
|
| O |
4 | O |
|
|
|
|
|
5 |
|
| O |
|
|
|
6 |
|
|
|
| O |
|
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
输入描述 Input Description
一个数字N表示棋盘是N x N大小的。
输出描述 Output Description
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
样例输入 Sample Input
6
样例输出 Sample Output
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
数据范围及提示 Data Size & Hint
6 <= N <= 13
#include<iostream>
using namespace std;
int n,sum,a[15];
bool b[100]={0},c[100]={0},d[100]={0};
void print()
{
sum++;//统计总方案数
if(sum<=3){//前三个方案打印
for(int i=1;i<=n;i++){
cout<<a[i];
if(i!=n)cout<<' ' ;
}
cout<<endl;
}
}
void queen(int i) // 做第i行
{
for(int j=1;j<=n;j++){ //穷举i行的j列
if((b[j]==0)&&(c[i+j]==0)&&(d[i-j+n]==0)){ //可放
a[i]=j;//第i行放置在j列
b[j]=1; //标记j列不再可放
c[i+j]=1; //标记副对角线及平等线不再可放
d[i-j+n]=1; //标记主对角线及平等线不再可放
if(i==n)print();//第n行都放好了,就打印
else queen(i+1); //搜索下一行
b[j]=0;//回溯时,清放置标志
c[i+j]=0;
d[i-j+n]=0;
}
}
}
int main()
{
cin>>n;
queen(1);//从第一行开始做
cout<<sum;
return 0;
}
【codevs1039】NOIP2001数的划分
题目描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。 例如:n=7,k=3,下面三种分法被认为是相同的。1,1,5; 1,5,1; 5,1,1;问有多少种不同的分法。
输入
输入:n,k (6<n<=200,2<=k<=6)
输出
输出:一个整数,即不同的分法。
样例输入
7 3
样例输出
4
提示
输入: 7 3
输出:4 {四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}
#include<iostream>
#include<cstdio>
using namespace std;
int n,k,ans=0,a[21];
void dfs(int t,int s,int b)
{
int i;
if(s==n&&t==k){ans++;return;}
if(s==n||t==k)return;
for(i=b;i<=n-s;i++)
if(s+i<=n)dfs(t+1,s+i,i);
}
int main()
{
int i;
cin>>n>>k;
dfs(0,0,1);
cout<<ans;
return 0;
}
整数拆分——组合的和
来源:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1261
题目描述
一个正整数N(N<=20)可以划分成若干个正整数的和的形式,例如5可以划分成以下几种形式:
5=1+1+1+1+1
5=1+1+1+2
5=1+1+3
5=1+2+2
5=1+4
5=2+3
5=5
编写一个程序,输入任意一个不大于20的整数N,按以上格式输出它的所有拆分。
输入
只有一行,包含一个整数N(N不超过20)。
输出
按样例中格式,输出对N的所有拆分。
样例输入
5
样例输出
5=1+1+1+1+1
5=1+1+1+2
5=1+1+3
5=1+2+2
5=1+4
5=2+3
5=5
提示
#include<iostream>
using namespace std;
int a[10001]={1},n,total;
int print(int t)
{
cout<<n<<"=";
for(int i=1;i<=t-1;i++)
cout<<a[i]<<"+";
cout<<a[t]<<endl;
}
int dfs(int s,int t)//s为剩余数,t为所填写的格子
{
int i;
for(i=a[t-1];i<=s;i++)//穷举可放数,当前格的数>=前一格,加法交换律
if(i<n){
a[t]=i;//第t格放i这个数
s-=i;
if(s==0)print(t);//剩余数为0,则打印
else dfs(s,t+1);
s+=i;
}
}
int main()
{
cin>>n;
dfs(n,1);//剩余数为n,从第一格开始做
cout<<n<<'='<<n;
return 0;
}
【codevs3895】环素数
题目描述 Description
给定一个N,求1——N组成的环,使得环上相邻的元素和为素数。
输入描述 Input Description
一个整数N
输出描述 Output Description
把1放在第一位置,按照字典顺序不重复地输出所有解(顺时针,逆时针算不同的两种),相邻两数之间严格用一个空格隔开,每一行的末尾不能有多余的空格。如果无解,则输出“no”。
样例输入 Sample Input
8
样例输出 Sample Output
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
数据范围及提示 Data Size & Hint
1<=N<=10
没过程序
#include<iostream>
#include<cstdio>
using namespace std;
int n,ans[21];
bool used[21];
void print()
{
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
bool is_prime(int x)
{
if(x==3||x==5||x==7||x==11||x==13||x==17||x==19)return 1;
return 0;
}
void search(int k)
{
if(k==n+1&&is_prime(ans[1]+ans[n])){print();return;}
for(int i=2;i<=n;i++){
if(used[i])continue;
if(k==1||is_prime(i+ans[k-1])){
ans[k]=i;used[i]=1;
search(k+1);
used[i]=0;
}
}
}
int main()
{
cin>>n;
ans[1]=1;used[1]=1;
if(n%2==0)search(2);
else cout<<"no";
return 0;
}
Luogu-P1595 信封问题
题目描述
某人写了n封信和n个信封,如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。
输入输出格式
输入格式:
一个信封数n
输出格式:
一个整数,代表有多少种情况。
输入输出样例
输入样例#1:
样例1:
2
样例2:
3
输出样例#1:
样例1:
1
样例2:
2
方法一:dfs
#include<iostream>
using namespace std;
int n,s,a[10001];
void dfs(int x)
{
if(x==n){
if(a[x]!=0)s++;//如果搜到最后一个都满足条件,计数器加一
return ;
}
for(int i=1;i<=n;i++)//枚举可能情况
if(a[i]==0&&i!=x){
a[i]++;
dfs(x+1);
a[i]--;
}
}
int main()
{
cin>>n;
dfs(1);
cout<<s;
return 0;
}
方法二:递推
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int n,m,f[100],sum=0;
cin>>n;
f[1]=0;f[2]=1;//初始化
for (int i=3;i<=n;i++){
f[i]=(i-1)*(f[i-1]+f[i-2]);// 递推
}
cout<<f[n];//输出结果
return 0;
}
Luogu-P1028 数的计算
题目描述
我们要求找出具有下列性质数的个数(包含输入的自然数n):
先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理:
1.不作任何处理;
2.在它的左边加上一个自然数,但该自然数不能超过原数的一半;
3.加上数后,继续按此规则进行处理,直到不能再加自然数为止.
输入输出格式
输入格式:
一个自然数n(n<=1000)
输出格式:
一个整数,表示具有该性质数的个数。
输入输出样例
输入样例#1:
6
输出样例#1:
6
说明
满足条件的数为
6,16,26,126,36,136
方法一:dfs
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int a[10001],n,ans=1; //总数的初始值是1,因为我在搜索时没有考虑输入的数本身
int dfs(int t)
{
int i;
for (i=1;i<=a[t-1]/2;i++){//下一位一定是1到前一位的一半
a[t]=i;//记录
ans++; //累计,这个值不用回复
dfs(t+1);
a[t]=0;
}
}
int main()
{
scanf("%d",&n);
a[1]=n;
dfs(2); //搜索
printf("%d",ans);
return 0;
}
方法二:递推
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int a[10001],n,ans=1;
int main()
{
int i,j;
scanf("%d",&n);
a[1]=1;
for(i=2;i<=n;++i){
a[i]=1;
for(j=1;j<=i/2;++j)
a[i]=a[i]+a[j];
}
printf("%d",a[n]);
return 0;
}
方法三:记忆化搜索
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
long long a[100005];
int n;
long long dfs(int s)
{
int i;
if(a[s]!=-1) return a[s];
long long ans=1;
for(i=1;i<=s/2;i++)
ans+=dfs(i);
a[s]=ans;
return ans;
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)a[i]=-1;
a[1]=1;
printf("%lld",dfs(n));
return 0;
}