题目意思:给定一个最大为4x4的棋盘,棋盘上面可以放着车还有代表墙的'X',要求对于两个车是不能够连成一条直线的,就是中间有'X'或者是两个的连线为折线
解题思路:1 暴力枚举解空间,求出解空间的最大的值 2 回溯法,通过试探每一点的放与不放,还有判断是否能够满足条件求出最后的最大值
代码1(暴力枚举):
//暴力枚举2^16种解
//我们知道对于这个搜索的解空间树的解集最多有2的16次方种,那么复杂度就比较小,我么可以去枚举每一个解,然后找到其中的一个最优解。对于这道题而言,每一个位置的状态就是放与不放,转化为0 1思想(1表示放,0表示不放),我们可以用一个数组最大16位,存储从第一层到最后一层的最后一个,这样每个点的状态就被表示出来了,如何得到这个数组呢,我们从最大数开始,对于n*n的而言,最大数为2^(n*n) -1,我们用二进制的思想每一次把数和1做&运算,然后逆向存储到数组里面,做完把数右移一位即可省区前面许多没用的位数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <list>
#include <vector>
#include <stack>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 4;
int n , cnt , pos , ans , flag;
bool bite[MAXN*MAXN];//存储点的状态
char G[MAXN][MAXN];//存储输入的地图
int dir[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};//方向数组
//判断是否满足条件
//如果碰到'X'直接退出,碰到'.'且bite值为1那么直接返回0,否则返回1
int judge(int x , int y){
for(int i = x-1 ; i >= 0 ; i--){
if(bite[i*n+y] &&G[i][y] == '.')
return 0;
if(G[i][y] == 'X')
break;
}
for(int i = x+1 ; i < n ; i++){
if(bite[i*n+y] &&G[i][y] == '.')
return 0;
if(G[i][y] == 'X')
break;
}
for(int i = y-1 ; i >= 0 ; i--){
if(bite[x*n+i] &&G[x][i] == '.')
return 0;
if(G[x][i] == 'X')
break;
}
for(int i = y+1 ; i < n ; i++){
if(bite[x*n+i] &&G[x][i] == '.')
return 0;
if(G[x][i] == 'X')
break;
}
return 1;
}
//每次传入一个数进行解的计算
void solve(int num){
int i , j;
flag = 0;
pos = n*n - 1;//pos指向bite数组的下标
memset(bite , 0 , sizeof(bite));
while(pos >= 0){
bite[pos] = num & 1;//每一和1&运算
--pos;//向前移动
num >>= 1;//右移一位
}
//判断是否满足,对于bite数组是1的才判断
for(i = 0 ; i < n ; i++){
for(j = 0 ; j < n ; j++){
if(bite[i*n+j]){
if(G[i][j] == 'X')//如果是'X'而该点为1则可以直接返回说明该解不可能有
return;
if(G[i][j] == '.'){//如果是'.'判断
if(judge(i , j) == 0)
return;
}
}
}
}
//如果满足那么计算出数组中1的个数
for(i = 0 ; i < n*n ; i++){
if(bite[i])
++flag;
}
}
//主函数
int main(){
while(scanf("%d%*c" , &n) && n){
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < n ; j++)
scanf("%c" , &G[i][j]);
getchar();
}
int m = n*n;
ans = 0;
cnt = pow(2 , m) - 1;//最大的数
while(cnt >= 0){//循环枚举解空间
solve(cnt);
--cnt;
ans = (ans > flag ? ans : flag);//求最大的ans
}
printf("%d\n" , ans);
}
return 0;
}
代码2(回溯搜索):
//我们可以用回溯来做,所谓的回溯就是在dfs上返回上一层时候多了个状态返回,其它都一样。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <list>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 5;
int n , ans , flag;
char G[MAXN][MAXN];
int vis[MAXN][MAXN]; //初始化'X'的位置为-1,其它为0
//判断是否满足条件
int judge(int x, int y) {
for (int i = x - 1; i >= 0; i--) {
if (vis[i][y] == 1)//如果是1说明不满足
return 0;
if (vis[i][y] == -1)//如果是'X'则退出
break;
}
for (int i = y - 1; i >= 0; i--) {
if (vis[x][i] == 1)
return 0;
if (vis[x][i] == -1)
break;
}
return 1;
}
//回溯搜索
void dfs(int i, int j, int max) {
if (max > ans)
ans = max;
while (i < n) {
if (j<n && G[i][j] == '.' && vis[i][j] == 0) {
flag = judge(i, j);
if (flag) {
vis[i][j] = 1;
dfs(i, j + 1, max + 1);
vis[i][j] = 0;
}
}
if (j >= n) {
++i;
j = 0;
}
else
++j;
}
}
//主函数
int main() {
//freopen("input.txt" , "r" , stdin);
while (scanf("%d%*c", &n) && n) {
memset(vis, 0, sizeof (vis));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%c", &G[i][j]);
if (G[i][j] == 'X') {
vis[i][j] = -1; //初始化vis数组
}
}
getchar();
}
ans = 0;
dfs(0, 0, 0); //调用函数(开始搜索子树)
printf("%d\n", ans);
}
return 0;
}