题目:
1000:翻转卡片
总时间限制: 3000ms 内存限制: 65536kB
描述:
现在有N*N张卡片,组成了一个N*N的矩阵。这些卡片或者正面放置或者反面放置。现在,Acer想对这些卡片做一些翻转操作,使得所有的牌在翻转之后都正面朝上。在每一次操作中,Acer只能对一个M*M的子矩阵里的所有卡片做一次正反面翻转,也即将这个M*M矩阵中正面放置的卡片变成反面朝上,反面放置的卡片变成正面朝上。Acer想知道最少通过几次操作能够实现他的目标。输入第一行,两个整数N和M。
接下来的N行里,每一行1个整数。如果这个整数位1,表示这张对应的卡片在初始状态下时正面放置的;否则这张卡片最初是反面放置的。
对于所有数据,0
输出一个整数,表示实现目标所需要的最小操作次数。如果不可能实现目标,请输出-1。
样例输入
4 2
1 1 0 0
0 0 1 1
1 1 1 1
0 0 0 0
样例输出
5
分析:
使用贪心和递归方法,从每一行开始对每个元素进行检查,如果是0(反面), 则对以该点作为 M * M的左上角进行反转,使得当前元素变为1
注意: M *M 已越界,并且还存在0元素,则判定为无法实现,输出-1
#include <stdio.h>
#define MAX_NUM 1000
int cards[MAX_NUM][MAX_NUM];
int time = 0;
int N, M;
void reverse(int row) {
if (row + M >N) { // 以row 行开始的,如果已不足M行,则检查剩下的row行开始,有没有存在还是反面的卡片
for (int i = row; i < N; i++) {
for (int j = 0; j < N; j++) {
if (cards[i][j] == 0) { //存在反面的卡片
time = -1;
return;
}
}
}
return;
}
for (int j = 0; j < N; j ++) {
if (cards[row][j] == 1) continue; // 开始为1的不处理,即正面
if (j+M > N) { // 如果存在cards[row][j] 并且, j + M 已经大于N, 即无法进行用M*M 矩阵覆盖
time = -1;
return;
}
// 开始覆盖, 从 row-> row+M, j -> j+M
time++;
for (int k = row; k < row + M ; k++) {
for(int p = j; p < j + M; p++) {
cards[k][p] =!cards[k][p];
}
}
}
reverse(row+1);
}
int main() {
//freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &M);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%d", &cards[i][j]);
}
}
reverse(0);
printf("%d", time);
return 0;
}