目录
1.排列
任务描述
1.设计算法从前m个大写字母(m≤26)种取出n个字母的所有排列(组合),并编程实现
输入格式
输入M N
1<=M=26, N<=M
输出格式
按字典序输出排列
注意:行末不输出多余空格
Sample Input
4 2
Sample Output
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C
代码实现
#include<stdio.h>
int ans[26];
int flag[26];//标记该字母是否已经用过
char a[26];
void search(int c,int n,int m){
if(c==n){
int i;
for(i=0;i<n-1;i++){
printf("%c ",a[ans[i]]);
}
printf("%c\n",a[ans[i]]);
}
else{
for(int i=0;i<m;i++){//从m个字母中选
if(flag[i]) continue;//如果已被引用则跳过这次循环
ans[c]=i;//存放答案
flag[i]=1;//标记i序号的字母已被引用
search(c+1,n,m);
flag[i]=0;
}
}
}
int main(){
int M,N;
scanf("%d %d",&M,&N);
for(int i=0;i<26;i++){
a[i]='A'+i;//a用来存放字母
}
search(0,N,M);
return 0;
}
2.子集合
任务描述
设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法
输入格式
输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值
接下来的1行中,有n个正整数,表示集合S中的元素
n<10,c<100
输出格式
输出所有满足条件的集合,当问题无解时,输出“No Solution!”。
注意:末尾不输出多余空格
按照输入顺序输出,比如第一个满足条件的子集合是[2 2 6],那么就先输出它,子集合内的数也按照输入顺序输出
Sample Input
5 10
2 2 6 5 4
Sample Output
2 2 6
6 4
代码实现
#include<stdio.h>
int w[10];//存放整数
int x[10];//存放0、1的标志
int ws=0;//目前选择的元素的和
int rs;//目前未选中的元素的和
int n;int c;
int count=0;//计算x中1的个数
void H(int t,int ws,int rs){
if(t>=n){//已经回溯到叶节点
if(ws==c){//需要判断是否为可行解,是则输出
for(int i=0;i<n;i++){
if(x[i]==1) count++;
}
for(int j=0;j<n;j++){
if(x[j]==1){
if(count==1){
printf("%d\n",w[j]);
}
else printf("%d ",w[j]);
count--;
}
}
}
}
else{//还没有回溯到叶节点
if(ws+w[t]<=c){//左孩子剪枝
x[t]=1;
H(t+1,ws+w[t],rs-w[t]);
}
if(ws+rs>=c){//右孩子剪枝
x[t]=0;
H(t+1,ws,rs-w[t]);
}
}
}
int main(){
scanf("%d %d",&n,&c);
for(int i=0;i<n;i++){
scanf("%d",&w[i]);
rs+=w[i];
}
H(0,ws,rs);
return 0;
}
3.TSP问题
任务描述
输入格式
第一行输入n,代表有n个城市。
接下来n行每行输入n个数,第i行j列的值代表i市到j市的距离,0代表城市之间不通
注意:起点和终点都为1
n<7,城市之间的距离都不超过100
输出格式
第一行输出最少的旅行费用
第二行输入旅行路径
(保证只有一条最短旅行路径)
Sample Input
4 0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
Sample Output
25
1 3 2 4 1
代码实现
#include<stdio.h>
int n;
int a[8][8];//记录距离
int r[8];//记录路径
int bestr[8]={0};//记录最优路径
int bestp=999;//最少花费
int cp=0;//当前花费
void swap(int i,int j){//用于交换
int temp=r[i];
r[i]=r[j];
r[j]=temp;
}
void TSP(int c,int cp){
if(c>n){
if((a[r[n]][1]+cp<bestp)&&(a[r[n]][1])){
bestp=cp+a[r[n]][1];
for(int i=1;i<=n;i++){
bestr[i]=r[i];
}
}
}
else{
for(int i=c;i<=n;i++){
if((a[r[c-1]][r[i]])&&(cp+a[r[c-1]][r[i]]<bestp)){
swap(r[i],r[c]);
TSP(c+1,cp+a[r[c-1]][r[c]]);
swap(r[i],r[c]);
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
}
r[i]=i;
}
TSP(2,cp);
printf("%d\n",bestp);
for(int i=1;i<=n;i++){
printf("%d ",bestr[i]);
}
printf("1");
return 0;
}
4.n皇后问题
任务描述
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
输入格式
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
输出格式
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
Sample Input
1 8
5 0
Sample Output
1 92
10
代码实现
#include<stdio.h>
#include<math.h>
int N;
int ans[10];
int a[10];//用来标记行是否已经有皇后
int n;//每次回溯的深度
int sum=0;//每次所得到的答案
int check(int k){//检查是不是符合条件
for(int j=1;j<k;j++){
if((abs(k-j)==abs(a[k]-a[j]))||a[k]==a[j]){
return 0;
}
}
return 1;
}
void dfs(int c){
if(c>n){
sum++;
}
else{
for(int j=1;j<=n;j++){
a[c]=j;//第c个皇后在j列
if(check(c)==1){
dfs(c+1);
}
}
}
}
int main(){
for(int i=1;i<=10;i++){
n=i;
dfs(1);
ans[i]=sum;
sum=0;
}
while(scanf("%d",&N)==1&&N!=0){
printf("%d\n",ans[N]);
}
}
5.0-1背包
任务描述
输入格式
第一行输入n,c,分别代表物品的数量和背包的容量
第二行输入n个数 分别代表每个物品的重量
第三行输入n个数,分别代表每个物品的价值
1<n<100,1<c<100,重量不超过100,价值不超过1000
输出格式0
输出一个数,代表能装入的最大价值
Sample Input
4 7
3 5 2 1
9 10 7 4
Sample Output
20
代码实现
#include<stdio.h>
int n;//物品数量
int c;//背包重量
int w[100];//物品重量
int v[100];//物品价值
int cp=0;//目前装了的总价值
int rp;//还没有装的物品的总价值
int cw=0;//目前装了的总重量
int bestp=0;//目前得到的最大的价值
void package(int i,int cp,int rp,int cw){
if(i>=n){//到达叶子节点时
if(cp>bestp){
bestp=cp;
}
}
else{
if(cw+w[i]<=c){//左剪枝
package(i+1,cp+v[i],rp-v[i],cw+w[i]);
}
if(cp+rp>bestp){
package(i+1,cp,rp-v[i],cw);
}
}
}
int main(){
scanf("%d %d",&n,&c);
for(int i=0;i<n;i++){
scanf("%d",&w[i]);
}
for(int i=0;i<n;i++){
scanf("%d",&v[i]);
rp+=v[i];
}
package(0,cp,rp,cw);
printf("%d",bestp);
return 0;
}