UVa 639 - Don't Get Rooked

36 篇文章 0 订阅

类似“八皇后问题”,但加了“墙”的概念,如果有墙相隔,则同一行墙两边可同时存在两个元素。

枚举子集+判断可行性,在枚举子集时,可以进行适当剪枝,如此可以减少很多不必要的枚举,比如:当n == 4时,其最大的的“车”的位置为8个,则我们可以加一个计数器,最多将子集的枚举量设为8,超过8则不再往子集中加入元素。

代码如下:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;

int n, num, num_1, cct;
int flag[20],save[5] = {0, 2, 3, 6, 9}; // n为1,2,3,4时对应的最大可行的“位置”个数
char a[5][5], c[5][5];
struct point
{
    int x, y;
} po[20];
int judge() // 判断子集是否可行
{
    memcpy(c, a, sizeof(a));
    for(int i = 0; i < num; i++)
        if(flag[i])
            c[po[i].x][po[i].y] = '*';
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            if(c[i][j] == '*')
            {
                for(int ii = i + 1, fl = 1; ii < n; ii++)
                {
                    if(c[ii][j] == 'X')
                        fl = 0;
                    else if(c[ii][j] == '*' && fl)
                        return 0;
                }
                for(int jj = j + 1, fl = 1; jj < n; jj++)
                {
                    if(c[i][jj] == 'X')
                        fl = 0;
                    else if(c[i][jj] == '*' && fl)
                        return 0;
                }
            }
    return 1;
}
int subset(int cur) // 对所有‘.’的位置进行枚举
{
    if(cur == num)
    {
        int fl = judge();
        if(fl && num_1 > cct)
            cct = num_1;
        return fl;
    }
    flag[cur] = 1;
    ++num_1;
    if(num_1 < save[n])
        subset(cur + 1);
    --num_1;
    flag[cur] = 0;
    subset(cur + 1);
    return 0;
}
int main()
{
#ifdef test
    freopen("sample.txt", "r", stdin);
#endif
    while(scanf("%d", &n), n)
    {
        cct = num_1 = num = 0;
        for(int i = 0; i < n; i++)
            scanf("%s", a[i]);
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if(a[i][j] == '.')
                {
                    po[num].x = i;
                    po[num++].y = j;
                }
        subset(0);
        printf("%d\n", cct);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值