POJ 2965 The Pilots Brothers' refrigerator

http://poj.org/problem?id=2965

方法一:

我是照着1753改的 直接从0到16枚举 有些多余的不影响运行的 代码就没删

不理解的话可以直接看一下1753的题解: https://www.cnblogs.com/dyhaohaoxuexi/p/10920790.html

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>

using namespace std;

int flag=0,ms[4][4];

void change(int newm[][4],int *b,int m)
{
    int i;
    for(i=0;i<m;i++)
    {
        int t=b[i];
        int tx=b[i]/4;
        int ty=b[i]%4;
        int j=0;
        for(j=0;j<4;j++)
        newm[tx][j]=!newm[tx][j];
        for(j=0;j<4;j++)
        newm[j][ty]=!newm[j][ty];
        newm[tx][ty]=!newm[tx][ty];   //本身这个点翻转了两次 一定要记得再翻转过来
    }
}

int panduan(int newm[][4],int *b,int m)
{
    int i,j;
    for(i=0;i<4;i++)
    for(j=0;j<4;j++)
    if(newm[i][j]!=1)
    {
        return 0;
    }
    cout<<m<<endl;
    for(i=0;i<m;i++)
    {
        cout<<b[i]/4+1<<' '<<b[i]%4+1<<endl;
    }
    exit(0);  //直接退出 不然可能会有多余的方案输出
}

void digui(int *a,int *b,int t,int now,int n,int m)
{
    int i,j;
    if(t==m)
    {
        int newm[4][4];
        for(i=0;i<4;i++)
        for(j=0;j<4;j++)
        newm[i][j]=ms[i][j];
        change(newm,b,m);
        if(panduan(newm,b,m))
        flag=1;
        return;
    }
    else
    {
        for(i=now;i<=n-(m-t);i++)
        {
            b[t]=a[i];
            digui(a,b,t+1,i+1,n,m);
        }
    }
}

int main()
{
    int i,j;
    char m[4][4];
    int a[16]={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15};
    int b[16];
    for(i=0;i<4;i++)
    {
        cin>>m[i];
        for(j=0;j<4;j++)
        {
            if(m[i][j]=='-')
            ms[i][j]=1;
            else
            ms[i][j]=0;
        }
    }
    for(i=0;i<=16;i++)
    {
        memset(b,0,sizeof(b));
        digui(a,b,0,0,16,i);
        if(flag==1)
        {
            break;
        }        
    }
}

方法二(相当精妙):

我是在这里看到的:
https://blog.csdn.net/qq_41552508/article/details/79837582

根据分析可以得出结论,把开关本身以及其同一行同一列的开关都进行一次操作,最终结果是只有开关本身状态发生变化,其他所有开关状态都不变。

策略找到之后,那我们就想,如果对于所有关闭着的开关都进行一次上述策略,那么肯定是能把冰箱打开的,下面我们要做的就是把一些无用的,重复的操作去掉即可。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
 
using namespace std;
 
int a[4][4];
 
int main()
{
    /*
    当我想把模块中某一个格子改变时,就需要把这个格子的行、列上每一个格子都改变一次
    所以没遇到一次‘+’就把这个格子行列上所有格子改变一次
    最后对于那些改变次数是奇数次的格子输出即可 
    */
    for(int i = 0;i < 4;i++)
    {
        string c;
        cin>>c;
        for(int j = 0;j < 4;j++)
        {
            if(c[j] == '+')
            {
                for(int k = 0;k < 4;k++)
                {
                    a[i][k]++;
                    if(k!=i)
                        a[k][j]++;
                }
            }
        }
    }
    int sum = 0; //记录奇数点个数 
    for(int i = 0;i < 4;i++)
    {
        for(int j = 0;j < 4;j++)
        {
            if(a[i][j] % 2 != 0)
            {
                sum++;
            }
        }
    }
    printf("%d\n",sum);
    for(int i = 0;i < 4;i++)
    {
        for(int j = 0;j < 4;j++)
        {
            if(a[i][j] % 2 != 0)
            {
                printf("%d %d\n",i+1,j+1);
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/10924862.html