传送门:
题面描述:
给你一个方格,这个方格中有M*N个小方块,每个小方块都是0或1,定义一种反转操作:,每次反转范围是中心方块以及其上下左右的方块,反转后状态变化为0->1,1->0。问能不能通过有限次反转操作,将方格的最终状态变为全是0,如果可以,输出一个反转次数最少而且字典序最小的矩阵,矩阵对应的数值为该位置的反转次数,否则,输出IMPOSSIBLE。
思路:
1.对于同一个位置来说,最多反转1次,因为只有两种状态,反转两次事实上跟原先状态是一样的。
2.对于同一个位置,它的最终状态会受其上下左右各个方块反转的影响,这会使问题的分析变得非常复杂,但是我们可以先确定第一行的反转次序,此时对于第一行来说,能够影响它的左中右的反转都已经确定了,那么决定他最终状态的就只有它的下的反转,调节他下面方块的反转次序,使第一行的状态全变成0,然后对于第二行一样,以此类推,到了倒数第一行,此时不仅确定了倒数第二行的状态,也把最后一行的反转次数确定了,同时,最后一行的状态也被确定了(可能有点绕),此时我们只要判断最后一行是不是全是0就可判断这种第一行的反转次序是不是可以满足条件。
3.接下来,我们来思考如何来枚举第一行的各种反转次数,由1可以知道,每个位置事实上只有0和1两种状态,所以我们可以考虑二进制枚举来解决问题,具体实现在AC代码里面。因为我们是从小到大枚举的,所以最终得到的结果一定是最小字典序的,所以不用再考虑字典序的问题了。
最终代码:
#include#include #include using namespace std;const int maxn = 20;int map[maxn][maxn],flip[maxn][maxn],ans[maxn][maxn],m,n;int dx[] = { 1,0,0,0,-1};int dy[] = { 0,1,0,-1,0};int get(int x,int y){ int c =map[x][y]; for(int d = 0;d<5;d++){ int x1 = x+dx[d]; int y1 = y+dy[d]; if(x1>=0&&x =0&&y >j&1; int num = calc(); if(num>=0&&(res<0||res>num)){ res = num; memcpy(ans,flip,sizeof(flip)); } } if(res==-1) printf("IMPOSSIBLE\n"); else { for(int i = 0;i
如还有困惑的地方,请在评论区留言交流!