Fliptile
解題思路
- 對於這個題目可以用遞推來寫
- 因為每次翻轉只會影響一個十字架的區域,所以如果我們知道第一行的情況,那麼是不是隻要把第一行的所有的1在對應的下一個行的相同位置進行翻轉就可以把第一行的所有的1變成0呢(重要性質)
- 那麼只要我們不斷遞推下去就可以得到最後一行的狀態,如果最後一行全是0,代表方案合法
- 那麼我們怎麼得到第一行的狀態呢?
- 很明顯第一行的狀態是固定的,只有2 ^ m列種情況
- 那麼我們只要列舉這2 ^ m列種情況就可以得到我們的答案
- 注意因為第一行的狀態可以由第一行的原始狀態,透過翻轉操作得到第一行所有狀態(注意列舉的是翻轉操作,不是第一行的狀態)
- 所有我們只要列舉翻轉的操作就可以了,同時題目也說叫我們列印翻轉的次數
程式碼實現
#include <iostream>
#include <cstring>
using namespace std;
const int N = 16;//矩陣最大規模
const int INF = 100000;//表示一個無窮大的數
int n;//矩陣的行數
int m;//矩陣的列數
int martix[N][N];//原始矩陣
int backup[N][N];//暫時存放原始矩陣,用來恢復現場
int ans[N][N];//最終的答案,即翻轉操作
int t[N][N];//每一次合法的答案
int d[5][2] = {
{1, 0}, {-1, 0}, {0, 0}, {0, -1}, {0, 1}
};
int res = INF;//操作的次數初始化為一個極大值
void filp(int x, int y)//翻轉x,y位置的值
{
for (int i = 0; i < 5; i++) {
int nx = x + d[i][0];
int ny = y + d[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
martix[nx][ny] ^= 1;
}
}
}
void solve()
{
//讀入矩陣
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> martix[i][j];
backup[i][j] = martix[i][j];
}
}
//列舉所有的操作
for (int i = 0; i < 1 << m; i++) {
memset(t, 0, sizeof(t));//給t陣列全部賦值為0
memcpy(martix, backup, sizeof(backup));//把backup陣列的內容複製到martix陣列
int cnt = 0;//統計翻轉操作的次數
//看第一行有那些位置需要翻轉
for (int j = 0; j < m; j++) {
if (i >> j & 1) {
filp(0, j);//翻轉第一行的j位置
cnt++;//翻轉了一次計數器加一
t[0][j] = 1;//計入翻轉的位置
}
}
//只看到第n - 1行,因為最後一行沒有燈了,所以推到n - 1行就可以了
for (int j = 0; j < n - 1; j++) {
for (int k = 0; k < m; k++) {
if (martix[j][k]) {//如果當前行的k位置是1,那麼我們要把它變成0,那麼就翻轉它的下一行的位置
filp(j + 1, k);//翻轉下一行來改變當前位置
t[j + 1][k] = 1;//翻轉的是下一行所以記錄j + 1位置
cnt++;//翻轉了一次計數器加一
}
}
}
bool islaw = 1;//判斷方案是不是能夠讓矩陣全為0,1代表合法即最後矩陣全為0,0代表不合法,矩陣沒有全為0
//遍歷最後一行
for (int j = 0; j < m; j++) {
if (martix[n - 1][j]) {//如果有一個1
islaw = 0;//方案不合法
break;//跳出迴圈
}
}
if (islaw) {//方案合法
if (cnt < res) {//並且比之前的答案的操作次數還要更少
res = cnt;//更新最少的操作次數
memcpy(ans, t, sizeof(t));//把新的答案複製到ans陣列裡面去
}
}
}
if (res != INF) {//如果res的值不是INF那麼代表有合法的方案
//列印翻轉運算元組
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << ans[i][j] << ' ';
}
cout << '\n';
}
}
else {//res == INF沒有合法的方案,列印不可能
cout << "IMPOSSIBLE";
}
}
int main()
{
cin >> n >> m;//讀入矩陣的行和列
solve();
return 0;
}
Shuffle'm Up
解題思路
- 一道模擬題,根據樣例我們可以看出,它是每次分半組合,然後一直迴圈下去,但如果再次碰到第一次組合的結果,就代表無法得到想要的結果
程式碼解析
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define u unsigned
#define ll long long
#define sc scanf
#define pr printf
#define fr(i, j, n) for (int i = j; i < n; i++)
#define N 101
int t;
int main(int argc, char* argv[])
{
sc("%d", &t);
for (int i = 1; i <= t; i++) {
int c;
char s1[N] = { 0 };
char s2[N] = { 0 };
char s3[2 * N] = { 0 };
char s4[2 * N] = { 0 };//³õʼ״̬
char s5[2 * N] = { 0 };
bool is = 0;
sc("%d%s%s%s", &c, s1, s2, s3);
for (int i = 0; i < c; i++) {
s4[2 * i] = s2[i];
s4[2 * i + 1] = s1[i];
}
for (int i = 0; i < 2 * c; i++) {
s5[i] = s4[i];
}
int ans = 1;
while (1) {
for (int i = 0; i < c; i++) {
s1[i] = s4[i];
s2[i] = s4[i + c];
}
for (int i = 0; i < c; i++) {
s4[2 * i] = s2[i];
s4[2 * i + 1] = s1[i];
}
ans++;
if (strcmp(s4, s3) == 0) {
break;
}
if (strcmp(s4, s5) == 0) {
is = 1;
break;
}
}
if (is) {
pr("%d %d\n", i, -1);
}
else {
pr("%d %d\n", i, ans);
}
}
return 0;
}