P6201 & P1985 Fliptile S 题解
Fliptile S & 翻转棋 Fliptile S
暴力-47pts
搜索每个格子翻或不翻,最后判断是否每个点都为零即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int dx[5] = {-1, 0, 0, 1, 0}, dy[5] = {0, -1, 1, 0, 0};
int n, m, a[20][20], ans[400], b[20][20], c[400], ansum;
bool f;
bool check(){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (b[i][j]){
return 0;
}
}
}
return 1;
}
void dfs(int x, int y, int t, int sum){
if (sum > ansum){
return ;
}
if (f && sum == ansum && c[t - 1] > ans[t - 1]){
return ;
}
if (x == n && y == m + 1){
if (check()){
if (!f || sum < ansum){
for (int i = 1; i < t; i++){
ans[i] = c[i];
}
f = 1;
return ;
}
for (int i = 1; i < t; i++){
if (c[i] < ans[i]){
for (int j = 1; j < t; j++){
ans[i] = c[i];
}
break;
} else if (c[i] > ans[i]){
break;
}
}
}
return ;
}
if (y == m + 1){
dfs(x + 1, 1, t, sum);
return ;
}
for (int i = 0; i < 5; i++){
b[x + dx[i]][y + dy[i]] = !b[x + dx[i]][y + dy[i]];
}
c[t] = 1;
dfs(x, y + 1, t + 1, sum + 1);
for (int i = 0; i < 5; i++){
b[x + dx[i]][y + dy[i]] = !b[x + dx[i]][y + dy[i]];
}
c[t] = 0;
dfs(x, y + 1, t + 1, sum);
}
int main() {
cin >> n >> m;
ansum = n * m + 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
cin >> a[i][j];
b[i][j] = a[i][j];
}
}
dfs(1, 1, 1, 0);
if (!f){
cout << "IMPOSSIBLE";
} else {
for (int i = 1; i <= n * m; i++){
cout << ans[i] << ' ';
if (i % m == 0){
cout << '\n';
}
}
}
return 0;
}
正解-100pts
首先,在字典序最小的情况下,翻的次数只有两种
- 翻奇数次相当于翻 \(1\) 次
- 翻偶数次相当于翻 \(0\) 次
当第一排固定之后,后面翻或不翻都是固定的。
- 当第一排固定后,如果上一行的对应位置为 \(1\),那么当前位置 必须 翻一次,依此类推
最后只需判断第 \(M\) 行是否全为零即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int dx[5] = {-1, 0, 0, 1, 0}, dy[5] = {0, -1, 1, 0, 0};
int n, m, a[20][20], ans[400], b[20][20], leb[20][20], c[400], sum, ansum;
bool f;
void s(int x, int sum){
if (x == m + 1){
int num = 0, t = m + 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
leb[i][j] = b[i][j];
}
}
// 之后每行都是固定的
for (int i = 2; i <= n; i++){
for (int j = 1; j <= m; j++){
if (b[i - 1][j]){
num++, c[t] = 1;
for (int k = 0; k < 5; k++){
b[i + dx[k]][j + dy[k]] = !b[i + dx[k]][j + dy[k]];
}
} else {
c[t] = 0;
}
t++;
}
}
bool flag = 1;
for (int i = 1; i <= m; i++){
if (b[n][i]){ // 有内鬼!
flag = 0;
break;
}
}
if (flag && sum + num < ansum){ // 没有内鬼并且翻的次数少些
f = 1;
ansum = sum + num;
for (int i = 1; i <= n * m; i++){
ans[i] = c[i];
}
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
b[i][j] = leb[i][j];
}
}
return ;
}
s(x + 1, sum); // 注意!先选不翻的情况才能使字典序最小
// 翻!
for (int j = 1; j < 5; j++){
b[1 + dx[j]][x + dy[j]] = !b[1 + dx[j]][x + dy[j]];
}
c[x] = 1;
s(x + 1, sum + 1);
// 回溯
c[x] = 0;
for (int j = 1; j < 5; j++){
b[1 + dx[j]][x + dy[j]] = !b[1 + dx[j]][x + dy[j]];
}
}
int main() {
cin >> n >> m;
ansum = n * m + 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
cin >> a[i][j];
b[i][j] = a[i][j];
}
}
s(1, 0);
if (!f){ // 不成立
cout << "IMPOSSIBLE";
} else {
for (int i = 1; i <= n * m; i++){
cout << ans[i] << ' ';
if (i % m == 0){
cout << '\n';
}
}
}
return 0;
}