POJ 3279 Fliptile[二进制状压DP]

题目链接【http://poj.org/problem?id=3279】

题意:给出一个大小为M*N(1 ≤ M ≤ 15; 1 ≤ N ≤ 15) 的图,图中每个格子代表一个灯泡,mp[i][j] = 0;表示(i,j)这盏灯灭,反之则亮。如果把反转(i,j)等的开关,那么这个灯和他上下左右的等都会变成原来相反的的状态。

问:用最少的使用开关的次数使得灯全部被熄灭。

思路:用0,1表示第一行每个位置是否使用开关,所有状态最多有(1<<15)-1种,如果第一行的状态确定了,其他行的开关状态也就确定了。即:当第一行开关反转过后,那么如果第一行(i,j)灯为0,呢么下一行(i+1,j)灯的开关就不能反转,反之必须反转,只有这样,当前行的灯才能被全灭。所以,枚举第一行的所有状态,逐层向下模拟,最后判断最后一行是否被全灭即可,同时维护最小值的状态即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int INF = 1e5;
const int maxn = 1 << 16;
int mp[17][17];
int t[17][17];
int ans[17][17];
int n, m;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
bool check()
{
    for(int j = 1; j <= m; j++)
        if(t[n][j])    return 0;
    return 1;
}
void init()
{
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            t[i][j] = mp[i][j];
}
int flip(int x, int y)
{
    t[x][y] ^= 1;
    for(int i = 0; i <= 3; i++)
    {
        int X = x + dir[i][0];
        int Y = y + dir[i][1];
        if(X >= 1 && X <= n && Y >= 1 && Y <= m)
            t[X][Y] ^= 1;
    }
}
int tap(int k)
{
    init();
    int num = 0;
    //处理第一行
    for(int i = 1; k; i++)
    {
        if(k & 1)
            flip(1, i), num++;
        k >>= 1;
    }
    //处理后面的行数
    for(int i = 1; i < n; i++)
        for(int j = 1; j <= m; j++)
        {
            if(t[i][j])
                flip(i + 1, j), num++;
        }
    return num;
}
void tap2(int k)
{
    init();
    memset(ans, 0, sizeof(ans));
    for(int i = 1; k; i++)//第一行
    {
        if(k & 1)
            flip(1, i), ans[1][i] = 1;
        k >>= 1;
    }
    for(int i = 1; i < n; i++)
        for(int j = 1; j <= m; j++)
        {
            if(t[i][j])
                flip(i + 1, j), ans[i + 1][j] = 1;
        }
}
int main ()
{
    while(~scanf("%d%d", &n, &m))
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &mp[i][j]);
        int mask = (1 << m) - 1;
        int T = INF;
        for(int i = 0; i <= mask; i++)
        {
            int num = tap(i);//反转
            if(check() && num < T)
            {
                T = num;
                tap2(i);//记录反转
            }
        }
        if(T == INF)
            printf("IMPOSSIBLE
");
        else
        {
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j < m; j++)
                    printf("%d ", ans[i][j]);
                printf("%d
", ans[i][m]);
            }
        }
    }
    return 0;
}
想的太多,做的太少。
原文地址:https://www.cnblogs.com/pealicx/p/6232313.html