poj2965 The Pilots Brothers' refrigerator

he Pilots Brothers’ refrigerator
Time Limit: 1000 ms / Memory Limit: 65536 kb
Description

The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator.
There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j.
The task is to determine the minimum number of handle switching necessary to open the refrigerator.

Input

The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “−” means “open”. At least one of the handles is initially closed.

Output

The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them.

Sample Input

-+--
----
----
-+--

Sample Output
6
1 1
1 3
1 4
4 1
4 3
4 4
Source
None

第一种解法,就是bfs+状态压缩,和我做的上道题十分类似,一直超时,这也让我学到了怎么用打表来减少时间,真的特别妙

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
#define N (1<<16)
typedef struct Node{
    int i,j;//代表扳动的哪个开关变成当前这个状态
    int front;//表示前面那个状态
    int count;//代表扳动了几个棋子
    bool visit;//防止反复出现该状态
}Node;
//这种打表思想来解决超时问题,真的太牛逼了
int flip[4][4] = {63624, 62532, 61986, 61713, 36744, 20292, 12066, 7953, 35064, 17652, 8946, 4593, 34959, 17487, 8751, 4383};
typedef struct num{
    int a,b;
    friend bool operator < (const num &p,const num &q)
    {
        if(p.a == q.a)
            return p.b < q.b;
        else
            return p.a < q.a;
    }
}num;
Node node[N + 1];
bool check(int i,int j)
{
    if(i < 0 || i > 3)
        return false;
    if(j < 0 || j > 3)
        return false;
    return true;
}
int main()
{
    //cout << N << endl;
    for(int i = 0;i <= N;++i)
    {
        node[i].i = node[i].j = node[i].front = -1;
        node[i].count = 0;
        node[i].visit = false;
    }
    int n = 0;
    for(int i = 0;i < 16;++i)
    {
        char s;
        cin >> s;
        n *= 2;
        if(s == '+')
            n += 1;
    }
    //cout << n << endl;
    queue<int>que;
    que.push(n);
    node[n].visit = true;
    int flag = -1;
    while(!que.empty() && flag == -1)
    {
        int t = que.front();
        que.pop();
        for(int i = 0;i < 4;++i)
        {
            for(int j = 0;j < 4;++j)
            {
                int m = t ^ flip[i][j];
                //如果要用下面的程序,每一次都要一位一位的进行异或,很费时间,当用打表出来的数,就可以直接异或将行列翻转
                /*int pos = 4 * i + j;
                m ^= (1 << (15 - pos));
                for(int a = 0;a < 4;++a)
                {
                    if(check(i,a) && a != j)
                        m ^= (1 << (15 + (j - a) - pos));
                }
                for(int a = 0;a < 4;++a)
                {
                    if(check(a,j) && a != i)
                        m ^= (1 << (15 + 4 * (i - a) - pos));
                }*/
                //cout << m << " " << (t ^ flip[i][j]) << endl;
                node[m].count = node[t].count + 1;
                if(m == 0)
                {
                    cout << node[m].count << endl;
                    flag = 0;
                }
                //相当于剪枝吧,接下来遍历的状态出现过,就不放到队列里了
                if(node[m].visit == false)
                {
                    node[m].visit = true;
                    node[m].i = i;
                    node[m].j = j;
                    node[m].front = t;
                    que.push(m);
                }
            }
        }
    }
    int x = flag;
    num ptr[17];
    int k = 0;
    while(node[x].front != -1)
    {
        ptr[k].a = node[x].i;
        ptr[k++].b = node[x].j;
        x = node[x].front;
    }
    for(int i = k - 1;i >= 0;--i)
    {
        printf("%d %d\n",ptr[i].a + 1,ptr[i].b + 1);
    }
    /*int x = flag;
    num ptr[N];
    int k = 0;
    while(node[x].front != -1)
    {
        ptr[k].a = node[x].i;
        ptr[k++].b = node[x].j;
        x = node[x].front;
        //cout << x << endl;
    }
    sort(ptr,ptr + k);
    for(int i = 0;i < k;++i)
    {
        cout << ptr[i].a + 1 << " " << ptr[i].b + 1 << endl;
    }*/
    return 0;
}

第二种解法来自大神,高手,神高效的解法,要使一个为’+’的符号变为’-‘,如果’+’位置对应的行和列上每一个位置都进行一次操作(就是对应行列开变关,关变开),则整个图只有这一’+’位置的符号改变,其余都不会改变.将翻转的个数成为操作数
首先要将初始状态的+号都变为-号,就是所在行列的每个位置都进行一次操作
然后对所有开关的操作数对2取余((因为一个点进行偶数次操作的效果和没进行操作一样,这就是楼上说的取反的原理),所以操作数为奇数的最小步骤中要扳动的开关

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[4][4];
bool mark[4][4];
typedef struct Node{
    int a,b;
}Node;
int main()
{
    memset(mark,false,sizeof(mark));
    for(int i = 0;i < 4;++i)
    {
        cin >> s[i];
    }
    for(int i = 0;i < 4;++i)
    {
        for(int j = 0;j < 4;++j)
        {
            if(s[i][j] == '+')
            {
                mark[i][j] = !mark[i][j];
                for(int k = 0;k < 4;++k)
                {
                    mark[i][k] = !mark[i][k];
                    mark[k][j] = !mark[k][j];
                }
            }
        }
    }
    int k = 0;
    Node node[17];
    for(int i = 0;i < 4;++i)
    {
        for(int j = 0;j < 4;++j)
        {
            if(mark[i][j] == true)
            {
                node[k].a = i;
                node[k++].b = j;
            }
        }
    }
    cout << k << endl;
    for(int i = 0;i < k;++i)
    {
        cout << node[i].a + 1 << " " << node[i].b + 1 << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值