1、N皇后
【问题描述】在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上)。
你的任务是,对于给定的N(N是正整数且6<=N<15),求出有多少种合法的放置方法。并输出前4个解。比如当N=6时,检查一个如下的6 x 6的棋盘,有六个皇后被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个皇后。
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个皇后,如下:
行号 1 2 3 4 5 6 列号 2 4 6 1 3 5 这只是跳棋放置的一个解。 【输入形式】 输入皇后的个数
【输出形式】
前四行为前四个解,每个解的两个数字之间用一个空格隔开。第五行只有一个数字,表示解的总数。
【样例输入】
6
【样例输出】
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 5 3 1 6 4 2 4
【样例说明】解按字典顺序排列。请输出前4个解。最后一行是6皇后的解的总个数。
【参考代码】
//#include <bits/stdc++.h>
#include<stdio.h>
#include <stdlib.h>
using namespace std;
int n,sum; //n表示输入的数,sum表示方案总数
//a[i]表示第i个皇后所占的列、b[i]表示第i列有没有被占
//c[i]表示左下到右上的对角线有没有被占、d[i]表示左上到右下的对角线有没有被占
int a[15],b[15],c[32],d[32];
void dfs(int m) //深搜,当前正在放置第m个皇后
{
if(m==n+1) //如果放置的皇后超出了n个,说明已放完了n个,方案数要+1
{
sum++; //sum既是总数也是前三个排列的判断
if(sum<=4) //只输出前三个解,如果解超出三个就不再输出
{
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
}
return;
}
for(int i=1;i<=n;i++)
{
if(!b[i]&&!c[m+i]&&!d[m-i+n]) //如果该列和两条对角线都没有被占领,放置皇后
{
a[m]=i; //第x个皇后占了第i列
b[i]=1; //标记占领了第i列
c[m+i]=1; //标记占领左下--右上对角线
d[m-i+n]=1; //标记占领左上--右下对角线
dfs(m+1); //深搜放置下一个皇后
b[i]=0; //回溯,清除标记
c[m+i]=0;
d[m-i+n]=0;
}
}
}
int main()
{
scanf("%d",&n);
dfs(1); //从第一个皇后开始放
printf("%d\n",sum); //输出总方案数
return 0;
}
2、子集和问题
【问题描述】子集和问题的一个实例为〈S,c〉。其中,S={ x1 , x2 ,…,xn }是一个正整数的集合,c是一个正整数。子集和问题判定是否存在S的一个子集S1,使得:
试设计一个解子集和问题的回溯法。
对于给定的正整数的集合S={ x1 , x2 ,…,xn }和正整数c,计算S 的一个子集S1,使得:
【输入形式】
输入数据的第1 行有2 个正整数n 和c(n≤10000,c≤10000000),n 表示S 的大小,c是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。
【输出形式】
将子集和问题的解输出。当问题无解时,输出“No Solution!”。
【样例输入】
5 10 2 2 6 5 4
【样例输出】
2 2 6
【参考代码】
#include<bits/stdc++.h>
using namespace std;
int a[100];//存储S的正整数集合
bool x[100];//记录各分支的结果情况
int w=0;//记录此时的子集和
bool flag=false;//记录是否有解
int n,c;
void backtrack(int i) {
if(i>n||flag==true)//到了最后一层或者已找到子集
return ;
//取出该层的数字
x[i]=true;
w=w+a[i];
//判断
if(w==c) {
for(int j=0; j<=i; j++) {
if(x[j]==true)
cout<<a[j]<<" ";
}
flag=true;
return ;
} else if(w<c) {
//接着向下取数
backtrack(i+1);
}
//回溯
x[i]=false;
w=w-a[i];
backtrack(i+1);
return ;
}
int main() {
cin>>n>>c;
for(int i=0; i<n; i++)
cin>>a[i];
backtrack(0);
if(flag==false)
cout<<"No Solution!\n";
return 0;
}
3、走迷宫
【问题描述】有一个m*n格的迷宫(表示有m行、n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,输入这m*n个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-1表示无路)。
【输入形式】
第一行是两个数m,n(1< m, n< 15),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。
【输出形式】
所有可行的路径,输出时按照左上右下的顺序。描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示。如果没有一条可行的路则输出-1。
【样例输入】
5 4 1 1 0 0 1 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 5 4
【样例输出】
(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4) (1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4) (1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4) (1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4) (1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4) (1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4) (1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
【参考代码】
#include <stdio.h>
#include <string.h>
int a[30][30];
int b[100];
int dir[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
int c,d,f;
void s(int g,int h,int k)
{
int i;
if(g==c&&h==d)
{
f=1;
for(i=0;i<k;i+=2)
{
printf("(%d,%d)",b[i],b[i+1]);
if(i<k)
printf("->");
}
printf("(%d,%d)\n",c,d);
return;
}
if(!a[g][h])
return;
for(i=0;i<4;i++)
{
a[g][h]=0;
b[k]=g;
b[k+1]=h;
s(g+dir[i][0],h+dir[i][1],k+2);
a[g][h]=1;
}
}
int main()
{
int n,m,g,h,i,j,k;
while(~scanf("%d %d",&n,&m))
{
k=f=0;
memset(b,0,sizeof(b));
memset(a,0,sizeof(a));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
scanf("%d %d %d %d",&g,&h,&c,&d);
s(g,h,0);
if(!f)
printf("-1\n");
}
return 0;
}