Fliptile POJ - 3279 题解优美的暴力

25 篇文章 0 订阅
11 篇文章 0 订阅

题目链接

https://vjudge.net/problem/POJ-3279

拿到这道题想了很久,都没找到什么好的做饭,一看题解,好家伙,直接暴力是没想到的,一看数据最多才15行15列,之间暴力就OK了。

首先,暴力第一行的所有可能性,2的15次方,大概32768种可能性。

只要第一行确定下来了,之后的情况都可以之间根据上一行来锁定出下一行,然后确定出整个矩阵的最优解。

注意,同样踩踏次数的可能性下,要选择字典序最小的那种踩踏情况,所以要从最小的数0开始,一直确定到最大的数2的15次方。

AC代码

#include <iostream>
#include <cstring>

using namespace std;

int M, N;
const int MAX_M = 15;
const int MAX_N = 15;
int tile[MAX_M][MAX_N];

int opt[MAX_M][MAX_N]; // 保存最优解
int flip[MAX_M][MAX_N];  // 保存中间结果

const int dx[5] = {-1, 0, 0, 0, 1};
const int dy[5] = {0, -1, 0, 1, 0};

// 查询(x, y)的颜色
// 这里当时出现了一点小错误,因为默认把x看做横坐标,y看做纵坐标了,其实不是这样的,下次x和y容易用反的话,还是用i和j来表示更舒服一点。
int get(int x, int y)
{
    int c = tile[x][y];
    for (int d = 0; d < 5; ++d)
    {
        int x2 = x + dx[d];
        int y2 = y + dy[d];
        if (0 <= x2 && x2 < M && 0 <= y2 && y2 < N)
        {
            c += flip[x2][y2];
        }
    }
    return c % 2;
}


// 求出第1行确定情况下的最小操作数
// 无解的情况返回-1
int calc()
{
    for (int i = 1; i < M; ++i)
    {
        for (int j = 0; j < N; ++j)
        {
            // 如果(i - 1, j)是黑色的话,则必须反转这个格子
            if(get(i - 1, j) != 0)
            {
                flip[i][j] = 1;
            }
        }
    }
    
    // 判断最后一行是否全为白色
    for (int j = 0; j < N; ++j)
    {
        if (get(M - 1, j) != 0)
        {
            return -1;
        }
    }
    
    // 统计翻转次数
    int res = 0;
    for (int i = 0; i < M; ++i)
    {
        for (int j = 0; j < N; ++j)
        {
            res += flip[i][j];
        }
    }
    return res;
}

void solve()
{
    int res = -1;
    
    // 按照字典序尝试第一行所有的可能性
    for (int i = 0; i < (1 << N); ++i)
    {
        memset(flip, 0, sizeof(flip));
        for (int j = 0; j < N; ++j)
        {
            flip[0][N - j - 1] = i >> j & 1;
        }
        int num = calc();
        if (num >= 0 && (res < 0 || res > num)) {
            res = num;
            memcpy(opt, flip, sizeof(flip));
        }
    }
    
    // 输出结果
    if (res < 0)
    {
        cout << "IMPOSSIBLE" << endl;
    }
    else
    {
        for (int i = 0; i < M; ++i)
        {
            for (int j = 0; j < N; ++j)
            {
                cout << opt[i][j];
                if (j < N - 1)
                {
                    cout << ' ';
                }
                else
                {
                    cout << endl;
                }
            }
        }
    }
}

int main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(false);
    
    cin >> M;
    cin >> N;
    
    for (int i = 0; i < M; ++i)
    {
        for (int j = 0; j < N; ++j)
        {
            cin >> tile[i][j];
        }
    }
    
    solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值