POJ 3279 Fliptile(DFS,想法)

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

题目大意:给定n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色)。我们需要把所有的格子都反转成白色,每反转一个格子,它上下左右的格子都会跟着反转。请求出用最小步数完成反转时每个格子反转的次数。有多个解时,输出字典序最小的一组。

Description

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.

As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".

Input

Line 1: Two space-separated integers:  M and  N 
Lines 2.. M+1: Line  i+1 describes the colors (left to right) of row i of the grid with  N space-separated integers which are 1 for black and 0 for white

Output

Lines 1.. M: Each line contains  N space-separated integers, each specifying how many times to flip that particular location.

Sample Input

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

Sample Output

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

Source


题目思路:最开始拿到这个题,哇,真的不知道应该咋搜索,想了很久都没想法,菜得嫌弃自己,去网上看大牛写的题解,突破口在于翻两次和不翻结果一样,要求步数最小,那么我们当然选择不翻,这样其实如果上一行的位置是1,那么下一行的x位置必须翻转,否则不需要翻转。这样我们就只用枚举第一行的所有情况(感觉也就这里用了一点点搜索,最主要是要有解题想法),复杂度2^m,然后判断最后是否全为0来判断是否可能成功。

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e6+5;

int n,m;
int mp[20][20];
int mapp[20][20]; //翻转数组
int ans[20][20];  //记录结果
int tmp[20][20];  //记录中间值
int minn;         //记录最小步数
int dir[4][2]={1,0,-1,0,0,1,0,-1};

void Copy(const int a[][20],int b[][20])
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            b[i][j]=a[i][j];
}

void change(int x,int y)
{
    //翻转x,y这个位置
    if(mapp[x][y]==1)
        mapp[x][y]=0;
    else
        mapp[x][y]=1;
    //翻转x,y周围位置
    for(int i=0;i<4;i++)
    {
        int next_x=x+dir[i][0];
        int next_y=y+dir[i][1];
        if(mapp[next_x][next_y]==1)
            mapp[next_x][next_y]=0;
        else
            mapp[next_x][next_y]=1;
    }
}

void solve()
{
    //这里要把第二行以后的tmp全部置为0,因为没翻转嘛
    //不初始化的话会保存上次结果,因为这个wa了三发 -.-
    for(int i=2;i<=n;i++)
        for(int j=1;j<=m;j++)
            tmp[i][j]=0;
    int cnt=0;              //记录翻转次数
    Copy(mp,mapp);          //复制一下mp数组给mapp用来翻转
    for(int i=1;i<=m;i++)   //判断第一行有几个1,翻转一下该位置旁边的
    {
        if(tmp[1][i]==1)
        {
            cnt++;
            change(1,i);    //翻转(i,j)这个位置及周围
        }
    }
    //现在翻转第一行一下的,如果上一下的x位置是1,那么下一行的x位置必需翻转
    //最后判断最后一行是否全0,全0说明满足要求
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(mapp[i-1][j]==1)
            {
                cnt++;
                tmp[i][j]=1;       //该位置需要翻转
                change(i,j);
            }
        }
    }
    bool flag=true;
    for(int i=1;i<=m;i++)
    {
        if(mapp[n][i]!=0)
        {
            flag=false;
            break;
        }
    }
    if(flag)
    {
        if(cnt<minn)
        {
            minn=cnt;
            Copy(tmp,ans);
        }
    }
}

void dfs(int x)  //枚举第一行的1
{
    if(x==m+1)
    {
        solve();
        return ;
    }
    else
    {
        tmp[1][x]=0;
        dfs(x+1);
        tmp[1][x]=1;
        dfs(x+1);
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //std::ios::sync_with_stdio(false);
    while(~scanf("%d%d",&n,&m))
    {
        MEM(mp,0);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&mp[i][j]);
        minn=INF;
        dfs(1);
        //printf("minn=%d\n",minn);
        if(minn==INF)
            printf("IMPOSSIBLE\n");
        else
        {
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    printf("%d%c",ans[i][j],(j==m)?'\n':' ');
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值