poj-3279 poj-1753(二进制枚举)

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

题目大意:

  有一个m*n的棋盘(1 ≤ M ≤ 15; 1 ≤ N ≤ 15),每个格子有两面分别是0或1,每次可以对一个格子做一次翻转操作,将被操作的格子和与其相邻的周围4个格子都会进行翻转。问做少做多少次翻转可以将所有格子翻转成0,输出翻转方案(每个棋子的翻转次数)。没有方案时输出“IMPOSSIBLE”。

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

解题思路:

  首先需要明确每个格子只有两种情况,就是要么翻和要么不翻,因为翻奇数次都与翻1次,而翻偶数次等于没翻。再来看一下数据范围n和m最大都为15,范围不是很大,但是全部枚举的话应该也会超时,所以我们要找到一个方案减少枚举量,我们发现如果一行的状态已经确定了,那么它接下来的是不是都确定了。下一行都要保证上一行全为0就可以了,最后只需要判断一下最后一行是否都为0就可以了,而第一行有n个棋子,它的状态也就是有2^n种。我们可以用一个含有16位的二进制数来表示它的状态,如果第i位为1表示该格子需要翻转,如果为0,则表示不翻转,翻转完第一行,仅接着翻第2行,每行的状态由其上一行的状态所确定。这样只要判断最后一行是否可以全部为0即可,如果为0,判断是否比答案更小,如果更小,则对答案进行更新。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
#include<cmath>
#include<list>
#include<deque>
#include<cstdlib>
#include<bitset>
#include<stack>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int n,m,mp[20][20],tmp[20][20],cnt[20][20],ans[20][20],res;
void flip(int x,int y)  //翻转,用异或符操作
{
    tmp[x][y]^=1;
    tmp[x+1][y]^=1;
    tmp[x-1][y]^=1;
    tmp[x][y+1]^=1;
    tmp[x][y-1]^=1;
}
int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>mp[i][j];
        }
    }
    res=INF;  //初始答案为无穷大
    for(int s=0;s<(1<<n);s++)  //枚举第一行的所有状态
    {
        int tot=0;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                tmp[i][j]=mp[i][j];
            }
        }
        for(int i=0;i<n;i++)
        {
            if(((s>>i)&1))  //如果第i+1位为1,则对其进行翻转
            {
                tot++;
                cnt[1][i+1]=1;
                flip(1,i+1);
            }
        }
        for(int i=2;i<=m;i++)  //翻转第2至m行
        {
            for(int j=1;j<=n;j++)
            {
                if(tmp[i-1][j])
                {
                    tot++;
                    cnt[i][j]=1;
                    flip(i,j);
                }
            }
        }
        int flag=1;
        for(int i=1;i<=n;i++)  //判断是否合法
        {
            if(tmp[m][i]!=0)
            {
                flag=0;
                break;
            }
        }
        if(flag&&tot<res)  //合法且比当前答案小对答案进行更新
        {
            res=tot;
            for(int i=1;i<=m;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    ans[i][j]=cnt[i][j];
                }
            }
        }
    }
    if(res==INF)  //不能实现
    {
        cout<<"IMPOSSIBLE"<<endl;
        return 0;
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(j==1) cout<<ans[i][j];
            else cout<<" "<<ans[i][j];
        }
        cout<<endl;
    }
    return 0;
}

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

题目大意:

  给你一个4*4的棋盘,上面摆了16个棋子,每个棋子有两面,一面是黑色一面是白色,给出棋盘的初始状态,每次可以翻一个棋子,而且每次翻的时候连着四周的棋子一起翻,问最少几次操作可以使棋盘全变为白棋或者黑棋。

思路:

  首先可以用DFS做,数据范围较小,最多翻16个棋子(每个棋子要么翻要么不翻,再翻就和原来一样了),枚举翻的棋子数,然后用DFS搜索翻该棋子数的所有情况,然后进行判断是否全为黑或全为白,就可以找出答案。

  当然也可以用二进制枚举的方法,总共16个棋子,对应2^16种状态,直接暴力枚举全部枚举全部情况,保留答案的最小值,与上面那题类似。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
#include<cmath>
#include<list>
#include<deque>
#include<cstdlib>
#include<bitset>
#include<stack>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int mp[5][5],tmp[5][5],ans;
void flip(int x,int y)  //翻转
{
    tmp[x][y]^=1;
    tmp[x+1][y]^=1;
    tmp[x-1][y]^=1;
    tmp[x][y+1]^=1;
    tmp[x][y-1]^=1;
}
int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);
    for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            char c;
            cin>>c;
            if(c==‘w‘) mp[i][j]=0;
            else mp[i][j]=1;
        }
    }
    ans=17;
    for(int s=0;s<(1<<16);s++)  //枚举所有情况
    {
        int tot=0;
        for(int i=1;i<=4;i++)
        {
            for(int j=1;j<=4;j++)
                tmp[i][j]=mp[i][j];
        }
        for(int i=0;i<16;i++)
        {
            if((s>>i)&1)
            {
                int x=(i+1)/4+1;  //判断第i+1棋子的坐标,然后对其翻转
                int y=(i+1)%4;
                if(y==0){x--;y=4;}
                tot++;
                flip(x,y);
            }
        }
        int flag=1;
        for(int i=1;i<=4;i++)  //判断是否合法
        {
            for(int j=1;j<=4;j++)
            {
                if(tmp[i][j]!=tmp[1][1])
                {
                    flag=0;
                    break;
                }
            }
            if(flag==0) break;
        }
        if(flag&&tot<ans)  //合法更新答案
            ans=tot;
    }
    if(ans==17)
        cout<<"Impossible"<<endl;
    else
        cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/zjl192628928/p/10031802.html

时间: 2024-10-11 02:01:17

poj-3279 poj-1753(二进制枚举)的相关文章

POJ 3279 Fliptile (二进制+搜索)

[题目链接]click here~~ [题目大意]: 农夫约翰知道聪明的牛产奶多.于是为了提高牛的智商他准备了如下游戏.有一个M×N 的格子,每个格子可以翻转正反面,它们一面是黑色,另一面是白色.黑色的格子翻转后就是白色,白色的格子翻转过来则是黑色.游戏要做的就是把所有的格子都翻转成白色.不过因为牛蹄很大,所以每次翻转一个格子时,与它上下左右相邻接的格子也会被翻转.因为翻格子太麻烦了,所以牛都想通过尽可能少的次数把所有格子都翻成白色.现在给定了每个格子的颜色,请求出用最小步数完成时每个格子翻转的

(简单) POJ 3279 Fliptile,集合枚举。

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 co

poj 3279 Fliptile(二进制)

http://poj.org/problem?id=3279 在n*N的矩阵上,0代表白色,1代表黑色,每次选取一个点可以其颜色换过来,即白色变成黑色,黑色变成白色,而且其上下左右的点颜色也要交换,求最少交换多少点能把全部换成颜色0 输出所需要换的点,用1表示,如果有多种方案,输出字典序足最小的方案 但是这题的数据里没有字典序的情况,所以没有比较字典序也可以过,我一开始就不知道这个怎么按字典序排 用二进制枚举第一行所有的情况,第一行的情况确定了,下面的就确定了 1 #include<cstdio

状态压缩+枚举 POJ 3279 Fliptile

题目传送门 1 /* 2 题意:问最少翻转几次使得棋子都变白,输出翻转的位置 3 状态压缩+枚举:和之前UVA_11464差不多,枚举第一行,可以从上一行的状态知道当前是否必须翻转 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 using namespace std; 9 10 const int MAXN = 20; 11 const int INF = 0x3f3f3f3

POJ 1681 Painter&#39;s Problem 【高斯消元 二进制枚举】

任意门:http://poj.org/problem?id=1681 Painter's Problem Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7667   Accepted: 3624 Description There is a square wall which is made of n*n small square bricks. Some bricks are white while some bric

POJ 2436 二进制枚举+位运算

题意:给出n头牛的得病的种类情况,一共有m种病,要求找出最多有K种病的牛的数目: 思路:二进制枚举(得病处为1,否则为0,比如得了2 1两种病,代号就是011(十进制就是3)),首先枚举出1的个数等于k的二进制数,然后跟所有的牛的代号一一比较,符合的           +1,找出其中和最大的:就是转换2进制麻烦,用位运算就好实现了,但是位运算不是很明白含义,明白了再补充: 知识点: 3 & 2 = 2,相同为1,不同为0, 011 & 010 = 010:(怎么利用的这个特点不明白):

poj 3279 Fliptile(二进制暴力)

Fliptile Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3629   Accepted: 1400 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

POJ 3279(Fliptile)题解

以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定长宽的黑白棋棋盘摆满棋子,每次操作可以反转一个位置和其上下左右共五个位置的棋子的颜色,求要使用最少翻转次数将所有棋子反转为黑色所需翻转的是哪些棋子(这句话好长...). [题目分析] 盯着奇怪的题目看了半天发现和奶牛没什么卵关系..奶牛智商高了产奶高是什么鬼说法... 这题刚开始被放到搜索的分类下了..然而这和搜索有什么关系..没经验所以想了各种和搜索沾边的方法,结果没想出解法,直到看了网上的题解,压根不是搜索啊有木有.

POJ 1161 Walls(最短路+枚举)

POJ 1161 Walls(最短路+枚举) 题目背景 题目大意:题意是说有 n个小镇,他们两两之间可能存在一些墙(不是每两个都有),把整个二维平面分成多个区域,当然这些区域都是一些封闭的多边形(除了最外面的一个),现在,如果某几个小镇上的人想要聚会,为选择哪个区域为聚会地点,可以使他们所有人总共需要穿过的墙数最小,题目上有说明,不在某个点上聚会(聚会点在某个多边形内部),行进过程中不穿过图中的点(也就是除出发点外的其他小镇). 输入第1行代表m(2<=M<=200)个区域 第2行代表n(3&

POJ 1730 Perfect Pth Powers (枚举||分解质因子)

Perfect Pth Powers Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16638   Accepted: 3771 Description We say that x is a perfect square if, for some integer b, x = b2. Similarly, x is a perfect cube if, for some integer b, x = b3. More g