POJ 1753 Flip Game(高斯消元法,枚举自由变元)

题目:

Flip Game

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 34731   Accepted: 15207

Description

Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it‘s black or white side up. Each
round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules:

  1. Choose any one of the 16 pieces.
  2. Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).

Consider the following position as an example:

bwbw

wwww

bbwb

bwwb

Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become:

bwbw

bwww

wwwb

wwwb

The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal.

Input

The input consists of 4 lines with 4 characters "w" or "b" each that denote game field position.

Output

Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it‘s impossible to achieve the
goal, then write the word "Impossible" (without quotes).

Sample Input

bwwb
bbwb
bwwb
bwww

Sample Output

4

Source

Northeastern Europe 2000

题意:有一个4*4的棋盘,上面放有黑白棋子,问最少翻转多少次可以使棋盘上的棋子都为黑色或都为白色。

思路:数据很小,可以暴力,但这里用高斯消元法求解,这题的难点在于要求最少次数,所以要枚举自由变元。假设有k个自由变元,那么所有的情况就有1<<k种,在高斯消元法求解的时候先记录下自由变元,然后对所有情况进行枚举,对非自由变元求解的时候方程有equ-k个。

代码:

#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include<climits>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <list>
#include <stdexcept>
#include <functional>
#include <utility>
#include <ctime>
using namespace std;

#define PB push_back
#define MP make_pair

#define REP(i,x,n) for(int i=x;i<(n);++i)
#define FOR(i,l,h) for(int i=(l);i<=(h);++i)
#define FORD(i,h,l) for(int i=(h);i>=(l);--i)
#define SZ(X) ((int)(X).size())
#define ALL(X) (X).begin(), (X).end()
#define RI(X) scanf("%d", &(X))
#define RII(X, Y) scanf("%d%d", &(X), &(Y))
#define RIII(X, Y, Z) scanf("%d%d%d", &(X), &(Y), &(Z))
#define DRI(X) int (X); scanf("%d", &X)
#define DRII(X, Y) int X, Y; scanf("%d%d", &X, &Y)
#define DRIII(X, Y, Z) int X, Y, Z; scanf("%d%d%d", &X, &Y, &Z)
#define OI(X) printf("%d",X);
#define RS(X) scanf("%s", (X))
#define MS0(X) memset((X), 0, sizeof((X)))
#define MS1(X) memset((X), -1, sizeof((X)))
#define LEN(X) strlen(X)
#define F first
#define S second
#define Swap(a, b) (a ^= b, b ^= a, a ^= b)
#define Dpoint  strcut node{int x,y}
#define cmpd int cmp(const int &a,const int &b){return a>b;}

 /*#ifdef HOME
    freopen("in.txt","r",stdin);
    #endif*/
const int MOD = 1e9+7;
typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<double> VD;
typedef long long LL;
typedef pair<int,int> PII;
//#define HOME

int Scan()
{
	int res = 0, ch, flag = 0;

	if((ch = getchar()) == '-')				//判断正负
		flag = 1;

	else if(ch >= '0' && ch <= '9')			//得到完整的数
		res = ch - '0';
	while((ch = getchar()) >= '0' && ch <= '9' )
		res = res * 10 + ch - '0';

	return flag ? -res : res;
}
/*----------------PLEASE-----DO-----NOT-----HACK-----ME--------------------*/

const int MAXN=50;

int a[MAXN][MAXN];//增广矩阵
int x[MAXN];//解集
int free_x[MAXN];//标记是否是不确定的变元

void Debug(int equ,int var)
{
    int i, j;
    for (i = 0; i < equ; i++)
    {
        for (j = 0; j < var + 1; j++)
        {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

inline int gcd(int a,int b)
{
    int t;
    while(b!=0)
    {
        t=b;
        b=a%b;
        a=t;
    }
    return a;
}
inline int lcm(int a,int b)
{
    return a/gcd(a,b)*b;//先除后乘防溢出
}

// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,
//-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
//有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
int Gauss(int equ,int var)
{
    int i,j,k;
    int max_r;// 当前这列绝对值最大的行.
    int col;//当前处理的列
    int ta,tb;
    int LCM;
    int temp;
    int free_x_num;
    int free_index;

    for(int i=0;i<=var;i++)
    {
        x[i]=0;
    free_x[i]=0;

    }
    free_x_num=0;
    //转换为阶梯阵.
    col=0; // 当前处理的列
    for(k = 0;k < equ && col < var;k++,col++)
    {// 枚举当前处理的行.
// 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
        max_r=k;
        for(i=k+1;i<equ;i++)
        {
            if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        }
        if(max_r!=k)
        {// 与第k行交换.
            for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
        }
        if(a[k][col]==0)
        {// 说明该col列第k行以下全是0了,则处理当前行的下一列.

            k--;
            free_x[free_x_num++]=col;
            continue;
        }
        for(i=k+1;i<equ;i++)
        {// 枚举要删去的行.
            if(a[i][col]!=0)
            {
                LCM = lcm(abs(a[i][col]),abs(a[k][col]));
                ta = LCM/abs(a[i][col]);
                tb = LCM/abs(a[k][col]);
                if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加
                for(j=col;j<var+1;j++)
                {
                    a[i][j] = a[i][j]^a[k][j];
                }
            }
        }
    }

  //Debug(equ,var);

    // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
    for (i = k; i < equ; i++)
    { // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.
        if (a[i][col] != 0) return -1;
    }
    // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
    // 且出现的行数即为自由变元的个数.
    //if (k < var)
   // {
        // 首先,自由变元有var - k个,即不确定的变元至少有var - k个.
      // Debug(equ,var);
        return var - k; // 自由变元有var - k个.
  //  }
    // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
    // 计算出Xn-1, Xn-2 ... X0.
   /* for (i = var - 1; i >= 0; i--)
    {
        temp = a[i][var];
        for (j = i + 1; j < var; j++)
        {
            if (a[i][j] != 0) temp ^= a[i][j] && x[j];
        }
        if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
        x[i] = temp / a[i][i];
    }
    return 0;*/
}

char mp[4][4];
int main()
{//freopen("out.txt","w",stdout);
for(int i=0;i<4;i++)
    {for(int j=0;j<4;j++)
    scanf("%c",&mp[i][j]);
    //printf("%c",mp[i][j]);}
   // printf("\n");
    getchar();
    }
MS0(a);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
    int k=i*4+j;
    if(mp[i][j]=='b')
        a[k][16]=0;
    else
        a[k][16]=1;
}
for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
{
    int t=i*4+j;
    a[t][t]=1;
    if(i!=0)
    a[(i-1)*4+j][t]=1;
    if(i!=3)
    a[(i+1)*4+j][t]=1;
    if(j!=0)
    a[i*4+(j-1)][t]=1;
    if(j%4!=3)
    a[i*4+j+1][t]=1;

}
int ans=Gauss(16,16);
if(ans==-1||ans==-2)
    ans=INT_MAX;
else
{   /*if(ans==0)
    {for(int i=0;i<16;i++)
        ans+=x[i];}*/
   // else
   // {
        int t=ans;
        ans=INT_MAX;
        int tot=(1<<t);

        for(int i=0;i<tot;i++)
            {   int cnt=0;
               int index=i;
                for(int j=0;j<t;j++)
            {
               x[free_x[j]]=(index&1);
               if(x[free_x[j]])
                cnt++;
               index>>=1;
            }

            for(int j=16-t-1;j>=0;j--)
            {
                int temp = a[j][16];
                //int tt=0;
                //while(a[j][tt]==0)
                   // tt++;
        for (int k = j+1; k < 16; k++)
        {
            if (a[j][k] != 0) temp ^=  x[k];
        }
       // if (temp % a[j][j] != 0) return -2; // 说明有浮点数解,但无整数解.
        x[j] = temp/a[j][j] ;
        cnt+=x[j];
            }
            ans=min(ans,cnt);
            }

   // }

}
MS0(a);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
    int k=i*4+j;
    if(mp[i][j]=='b')
        a[k][16]=1;
    else
        a[k][16]=0;
}
for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
{
    int t=i*4+j;
    a[t][t]=1;
    if(i!=0)
    a[(i-1)*4+j][t]=1;
    if(i!=3)
    a[(i+1)*4+j][t]=1;
    if(j!=0)
    a[i*4+(j-1)][t]=1;
    if(j%4!=3)
    a[i*4+j+1][t]=1;

}
int ans2=Gauss(16,16);
if(ans2==-1||ans2==-2)
    ans2=INT_MAX;
else
{
    /*if(ans2==0)
    {for(int i=0;i<16;i++)
        ans2+=x[i];}
    else
    {*/
        int t=ans2;
        ans2=INT_MAX;
        int tot=(1<<t);

        for(int i=0;i<tot;i++)
            {   int cnt=0;
                 int index=i;
                for(int j=0;j<t;j++)
            {

                    x[free_x[j]]=index&1;
                    if( x[free_x[j]])
                    cnt++;
                    index>>=1;
                }

            for(int j=16-t-1;j>=0;j--)
            {
                int temp = a[j][16];
              //  int tt=0;

               // while(a[j][tt]==0)
                 //   tt++;
        for (int k = j+1; k < 16; k++)
        {
            if (a[j][k] != 0) temp ^=  x[k];
        }
       // if (temp % a[j][j] != 0) return -2; // 说明有浮点数解,但无整数解.
        x[j] = temp/a[j][j] ;
        cnt+=x[j];
            }
            ans2=min(ans2,cnt);
            }

    //}

}
int res=min(ans,ans2);
if(res==INT_MAX)
{
    printf("Impossible\n");
}
else
    printf("%d\n",res);

        return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 02:38:32

POJ 1753 Flip Game(高斯消元法,枚举自由变元)的相关文章

POJ 1753 Flip Game (DFS + 枚举)

题目:http://poj.org/problem?id=1753 这个题在开始接触的训练计划的时候做过,当时用的是DFS遍历,其机制就是把每个棋子翻一遍,然后顺利的过了,所以也就没有深究. 省赛前一次做PC2遇到了几乎一模一样的题,只不过是把棋盘的界限由4X4改为了5X5,然后一直跑不出结果来,但是当时崔老湿那个队过了,在最后总结的时候,崔老湿就说和这个题一样,不过要枚举第一行进行优化. 我以为就是恢复第一行然后第二行以此类推,不过手推一下结果是6不是4,就知道这个有问题. 问了崔老湿,问了+

POJ 1753 Flip Game(枚举+DFS)

Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it's black or white side up. Each round you flip 3 to 5 p

POJ 1753 Flip Game (高斯消元 枚举自由变元求最小步数)

题目链接 题意:4*4的黑白棋,求把棋全变白或者全变黑的最小步数. 分析:以前用状态压缩做过. 和上题差不多,唯一的不同是这个终态是黑棋或者白棋, 但是只需要把给的初态做不同的两次处理就行了. 感觉现在还只是会套模板,不能独立的思考,好伤心.... 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath&g

[ACM] POJ 1753 Flip Game (枚举,BFS,位运算)

Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29921   Accepted: 12975 Description Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the

POJ 1753 Flip Game(dfs+枚举)

POJ 1753 题意: 输入一个4*4的图像,由黑白两色组成,定义一种操作为:改变某个格子内小球的颜色(黑变白,白变黑),同时其上下左右的格子内小球也将变色.求最少多少次操作能使之成为纯色图案. 思路: 对一个格子操作偶数次等于没有操作,操作奇数次等于操作一次,所以答案在0~16以及impossible之间. 从n=0开始枚举n次操作可能的组成情况,即操作哪几个格子,若某种组合能变图案为纯色则停止. 由于总组合数达到2^16,故枚举组合情况可以用dfs来进行回溯枚举. //还有一种方法是位运算

poj 1753 Flip Game (bfs + 枚举)

链接:poj 1753 题意:这是翻棋游戏,给定4*4棋盘,棋子一面为黑色(用b表示),另一面为白色(用w表示),问至少要几步可以将棋子翻为全黑或者全白,如不能达到目的,输出"Impossible " 翻转规则:可以选定16个棋子中的任意一个,将其本身以及上下左右相邻的翻转过来 分析:其实每格棋子最多只可以翻转一次(实际是奇数次,但与翻转一次状态一样),只要其中一格重复翻了2次(不论是连续翻动还是不连翻动),那么它以及周边的棋子和没翻动时的状态是一致的,与最初状态未翻转一样,由此就可以

POJ 1753 Flip Game (迭代递归)

POJ 1753,题目链接http://poj.org/problem?id=1753. POJ 1753,题目链接http://poj.org/problem?id=1753. 总共有16个点.对某个点选择操作或者不操作.一共有2^16次方种可能. 从前往后枚举,保证不重复. 1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 #includ

poj 1753 flip[ 枚举 ]

传送门:http://poj.org/problem?id=1753 思路:16格用16位的int表示,然后用bfs的层次关系枚举: 代码: #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #de

POJ 1753 Flip Game(二进制枚举)

题目地址链接:http://poj.org/problem?id=1753 题目大意: 有4*4的正方形,每个格子要么是黑色,要么是白色,当把一个格子的颜色改变(黑->白或者白->黑)时,其周围上下左右(如果存在的话)的格子的颜色也被反转,问至少反转几个格子可以使4*4的正方形变为纯白或者纯黑? 解题思路: 因为只有16个格子,且只有黑白两种状态,想到用一个二进制整数来表示棋盘的状态.首先你需要明白这个题目的两个性质——任何一个格子翻偶数次等同于不翻:翻格子的顺序对最终的结果是没有影响的,也就