给定一个N*M的矩阵,在矩阵中每一块有一张牌,我们假定刚开始的时候所有牌的牌面向上。
现在对于每个块进行如下操作:

翻转某个块中的牌,并且与之相邻的其余八张牌也会被翻转。
XXX
XXX
XXX
如上矩阵所示,翻转中间那块时,这九块中的牌都会被翻转一次。
请输出在对矩阵中每一块进行如上操作以后,牌面向下的块的个数。

方法1:常规贪心法(数据量太大超内存)

#include<iostream>
#include<vector>
using namespace std;
/*
整体思路:贪心算法
对每一块进行翻转,并检查其3*3范围内的牌是否符合翻转要求,若符合,则也进行翻转。
在这里用偶数0表示初始状态,每翻转一次+1,直到最后,检查奇数的个数即为结果
*/
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n, m;
        cin >> n >> m;
        //创建n*m的矩阵,初始化为0
        vector<vector<int> > poker(n, vector<int>(m, 0));
        //创建3*3范围内的dx,dy
        vector<int> dx = { -1,-1,-1,0,0,1,1,1 };
        vector<int> dy = { -1,0,1,-1,1,-1,0,1 };
        //对poker矩阵中的每一个点翻转,并判断其3*3邻域内点是否需要翻转
        for (int x = 0; x < n; x++)
        {
            for (int y = 0; y < m; y++)
            {
                //首先自己先翻转
                poker[x][y] += 1;
                //再判断3*3邻域内点是否要翻转,满足限制条件即可
                for (int dxy = 0; dxy < dx.size(); dxy++)
                {
                    int nextx = x + dx[dxy], nexty = y + dy[dxy];
                    if (nextx >= 0 && nextx < n && nexty >= 0 && nexty < m)
                    {
                        poker[nextx][nexty] += 1;
                    }
                }
            }
        }
        int sumOdd = 0;
        for (int x = 0; x < n; x++)
        {
            for (int y = 0; y < m; y++)
            {
                if (poker[x][y] % 2 != 0)
                    sumOdd += 1;
            }
        }
        cout << sumOdd << endl;
        poker.clear();
    }
}

方法2:找规律

#include<iostream>
#include<vector>
using namespace std;
/*
整体思路:找规律
观察一个点在以自己为中心的3*3邻域内有多少个邻居。
若有奇数个邻居,则这奇数个邻居的翻转会影响此点,邻居影响奇数次,而且此点也会自己翻转一次。所以总翻转次数为偶次,保持不动
若有偶数(包括零)个邻居,则它的偶数个邻居翻转会影响此点,邻居影响偶数次,自己会翻转一次,所以总翻转次数为奇数次,变化。
总结:若邻居为奇数,保持不变;若邻居为偶数,变化。
所以此题可以改换成找邻居为偶数个点的个数:
1*1类型的:0
1*n或n*1类型的(n>1):n-2
n*m类型(n>1且m>1):(n-1)*(m-1)
*/
int main()
{
    long long t;
    cin>>t;
    while(t--)
    {
        long long n,m;
        cin>>n>>m;
        if(n==1&&m==1)
            cout<<1<<endl;
        else if((n==1&&m>1)||(n>1&&m==1))
            cout<<max(n,m)-2<<endl;
        else
            cout<<(n-2)*(m-2)<<endl;
    }
    return 0;
}