【ICPC-190】uva 639 - Don't Get Rooked

点击打开链接

 

题目意思:给定一个最大为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;
}



 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值