フリップゲーム(2021-TRN1-K)
一般的なアイデア
これは、
チェスの駒が4 * 4のチェス盤(チェス盤と呼びましょう)の各グリッドに配置されるという点で、poj 1222(実際、質問は少し難しい)に少し似ています。チェスの駒は特別で、片面は黒、もう片面は白です。チェスの駒の色は、チェスの駒をひっくり返すことで変更できます。ピースを裏返すたびに、ピースの色が変わる必要があり、それに応じてピースの周りのピースの色も変わります。
最小限の手順を探します。
問題分析
一般的なDFSは、あまり多くのプルーニングを必要としません。DFSの本質は列挙型であるため、この結論が導き出されます。明らかに、各チェスの駒を開くか開かないかを選択でき、合計2 ^ 16の可能性があります。計算量は非常に少ないです。poj 1222のグリッドが増えると、暴力的なDFSが時間外になる可能性があります。
列挙する方法は?左から右、上から下です。開かれた場合と開かれていない場合の2つのケースが列挙されます。この種の列挙は前代未聞ではありません。その後、バックトラックします。
しかし、それをやったときはまだ苦労していました。チェスの駒の一部が開かないことに気づきませんでした。また、しばらくの間DFSを作成しました。手が生まれました。ステップも関数パラメーターとして使用されます。それ以外の場合は最初にさかのぼる必要がありますか?はっきりと考えていなかった、人がいなくなった。。。
コード
#include <bits/stdc++.h>
using namespace std;
int a[5][5];
char str[5][5];
int dir[4][2]={
{
1,0},{
0,1},{
-1,0},{
0,-1}};
int mini=1e8;
int check()
{
int i,j,sum=0;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++)
{
sum+=a[i][j];
}
}
if(sum==0||sum==16) return 1;
return 0;
}
void flip(int x,int y)
{
a[x][y]^=1;
for(int k=0;k<4;k++)
{
int newx=x+dir[k][0];
int newy=y+dir[k][1];
if(newx>=1&&newx<=4&&newy>=1&&newy<=4)
{
a[newx][newy]^=1;
}
}
}
void dfs(int x,int y,int step)
{
if(step>16) return;///16个格子都翻过了,不用继续深搜
if(check())
{
if(step<mini) mini=step;
return;
}
if(y>=5)
{
x++;
y=1;
}
if(x>=5) return;
flip(x,y);
dfs(x,y+1,step+1);///翻开棋子
flip(x,y);///回溯
dfs(x,y+1,step);///不翻开棋子
}
void init()
{
int i,j;
for(i=1;i<=4;i++)
{
for(j=0;j<4;j++)
{
if(str[i][j]=='b') a[i][j+1]=1;
else a[i][j+1]=0;
}
}
}
int main()
{
int i;
for(i=1;i<=4;i++)
{
gets(str[i]);
}
init();
dfs(1,1,0);
if(mini<=16) printf("%d\n",mini);
else printf("Impossible\n");
return 0;
}