[kuangbin带你飞]专题一 简单搜索 D - Fliptile(搜索+贪心)

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



题意:类似开关灯问题,题目给了一个M*N的二进制矩阵,可以选择任意一个点将它翻转,同时和它相邻的(上下左右)位置都需要翻转,找出一种翻转次数最少的方案,输出一个M*N的矩阵,每个位置的值代表其翻转次数
思路:第一排可以决定第二排的翻转状态,因为只有接下来的一排才能改变上一排灯的状态,使得第一排的灯都关掉;依此类推,第一排就可以决定整个矩阵的翻转状态。
实现:枚举第一排(i=0)的灯按与不按的方案(用搜索或者位运算),然后从i=1开始从左到右遍历,如果(i-1,j)灯没关,那么(i,j)就需要进行翻转

代码里的注释很详细,我相信聪明的你们一定都能看懂!

有个坑磨了我好久,可能有好几种最优方案,比如给的样例其实还有一种最优解法是:
0 1 1 0
0 0 0 0
0 0 0 0
0 1 1 0
但是题目只要最后一个最优方案ORZ

#include <iostream>
#include <cstring>  
/* 经典的开关灯问题,题目给了一个M*N的二进制矩阵,可以选择任意一个点将它翻转,同时和它相邻的(上下左右)位置都需要翻转,
找出一种翻转次数最少的方案,输出一个M*N的矩阵,每个位置的值代表其翻转次数 */

//思路:枚举第一排的灯按与不按的方案(用搜索或者位运算),如果第一排还有灯没关(i,j),那么下一排(i-1,j)就需要进行翻转 

//数据范围:(1 ≤ M ≤ 15; 1 ≤ N ≤ 15) 
 
using namespace std;
#define MAX 20
int Matri[MAX][MAX];  //灯矩阵
int cp[MAX][MAX];  //复制灯矩阵
int temp[MAX][MAX];  //临时方案矩阵
int result[MAX][MAX];  //最终方案矩阵 
int step = 99999999;  //最小翻转次数
int M,N; //M行N列

//cur:当前考虑行  sum:总反转次数 根据第一排灯的情况,接下来几排的操作,也就是对一种方案的试探 
 void dfs(int cur,int sum){
    if(cur==M){  //试验完一次之后
        int flag = 1;
        for(int i=0;i<N;i++){
            if(cp[M-1][i]==1)  //最后一排还有灯亮着,该方案不可行
                flag = 0; 
        }
        //最后一排所有灯都熄灭了,看它是不是比之前的方案更好(即总翻转次数更少) 
        if(flag&&sum<=step){
            step = sum;
            for(int i=0;i<M;i++){
                for(int j=0;j<N;j++)
                    result[i][j] = temp[i][j];    
             }
        }
        return;
    }else{
        for(int k=0;k<N;k++){
            if(cp[cur-1][k]==1){
                temp[cur][k]=1;
                sum++;
                
                cp[cur][k] = !cp[cur][k];  //自身翻转
                cp[cur-1][k] = !cp[cur-1][k];  //上一行翻转
                if(cur+1<M) cp[cur+1][k] = !cp[cur+1][k];  //下一行翻转
                if(k-1>=0) cp[cur][k-1] = !cp[cur][k-1];  //左边翻转
                if(k+1<N) cp[cur][k+1] = !cp[cur][k+1];  //右边翻转
                
            }else{ 
                temp[cur][k]=0;
            }
        }
        dfs(cur+1,sum);  //继续更新下一行
    }
 }
//先把第一排的方案执行
void op(){ for(int k=0;k<N;k++){ if(temp[0][k]==1){ cp[0][k] = !cp[0][k]; //自身翻转 if(M>1) cp[1][k] = !cp[1][k]; //下一行翻转 if(k-1>=0) cp[0][k-1] = !cp[0][k-1]; //左边翻转 if(k+1<N) cp[0][k+1] = !cp[0][k+1]; //右边翻转 } } } //k:当前考虑的列 sum:第一行总反转次数 void todfs(int k, int sum){ if(k==N){ //已经生成了第一排的方案 op();//先把第一行的方案执行 dfs(1,sum); //从第二行开始 memcpy(cp,Matri,sizeof(Matri));//试验完之后恢复成原矩阵,供下一种方案试验 return; }else{ temp[0][k] = 1;//翻转 todfs(k+1,sum+1); temp[0][k] = 0; //不翻转 todfs(k+1,sum); } } int main(int argc, char** argv) { while(scanf("%d %d",&M,&N)!=EOF){
     //初始化灯矩阵
for(int i=0;i<M;i++){ for(int j=0;j<N;j++){ scanf("%d",&Matri[i][j]); } } step = 99999999; //先设定一个不可能的值作为最小反转次数,如果这个值没有被更新过,说明翻转矩阵无解 memcpy(cp,Matri,sizeof(Matri)); //复制灯矩阵 memset(temp,0,sizeof(temp)); //初始化方案矩阵 //对第一排方案进行搜索 todfs(0,0); if(step!=99999999){ for(int i=0;i<M;i++){ for(int j=0;j<N-1;j++) printf("%d ",result[i][j]); cout<<result[i][N-1]<<endl; } }else cout<<"IMPOSSIBLE"<<endl; } return 0; }

 



转载于:https://www.cnblogs.com/jiaqizhang/p/8893614.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值