题解 P8549 小挖的核燃料填充
不错的dfs练手题。因为作者的 dfs 很菜,所以我们从头讲起。
前情提要:这篇题解建议配合代码食用,可以根据题解一行一行理解代码,我基本上都有讲。
首先我们考虑怎么搜索。单独考虑每一个元素显然是困难的,但是题目给了
我们搜索的时候考虑传三个参数
小矩阵的行、列可能不是特别好理解,举个例子,在样例一中。
这个具体如何实现呢?其实只需要正常按照
回到搜索本身,我们先按枚举行,再在行确定的情况下枚举列,那么 dfs 的结束条件就是当
那么这引入了一个问题,如何记录答案。STL 中有这样一个容器 pair,大概可以理解为一个类似结构体的东西,只不过结构体里面只有两个元素。这个刚好就可以用来解决这个问题,pair 当中的两个元素分别用于计算这个矩阵的行和列。
但是单用 pair 肯定是不够的,因为一个 pair 显然没办法记录整个答案。那么我们考虑再加一个容器,想到搜索实际上是用栈来实现的,所以可以考虑用栈。数组也是可以的,但是记录答案的大小,也就是代价不如栈方便,数组还要单独开一个变量记录大小并且实时更新而栈直接压进去就好了。
再次回到搜索,我们考虑枚举一个小矩阵转
接下来怎么搜索解决了,问题还有两个,第一个是如何判断合法,第二个是如何旋转。
首先判断合法是容易的,根据题目要求,只需判断这个矩阵里有没有重复的数字即可。为什么不用判断缺少的数字?因为有重复数字代表着有缺少的数字。
如何旋转是类似于一个坐标变换的东西,在题解刚开始的时候提到过获取小矩阵内每一个元素的位置只需要将其
至此,这道题就被解决了。有一个小优化,因为每行之间是独立的,所以如果上一个矩阵改完是不合法的,那么就直接返回。
细节还是很多的,大家可以参考一下。
#include<bits/stdc++.h>
using namespace std;
const int N=100;
char c;
int a[N][N];
int vis[N];
int tmp1[N][N];
int tmp2[N][N];
int ans=0x3f3f3f3f;
int n;
stack<pair<int,int> > an,da;
void change(int x,int y)//反转第 i 行,第 j 列的矩阵
{
memset(tmp1,0,sizeof(tmp1));
memset(tmp2,0,sizeof(tmp2));
for(int i=(x-1)*n+1;i<=n*x;i++)
{
for(int j=(y-1)*n+1;j<=n*y;j++)
{
tmp1[(i-1)%n+1][(j-1)%n+1]=a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
tmp2[n+1-j][i]=tmp1[i][j];
}
}
for(int i=(x-1)*n+1;i<=n*x;i++)
{
for(int j=(y-1)*n+1;j<=n*y;j++)
{
a[i][j]=tmp2[(i-1)%n+1][(j-1)%n+1];
}
}
}
bool check(int x,int y) //判断第 i 行,第 j 列的矩阵是否合法
{
for(int i=1;i<=n*x;i++)
{
memset(vis,0,sizeof(vis));
for(int j=1;j<=n*y;j++)
{
if(vis[a[i][j]]) return 0;
vis[a[i][j]]=1;
}
}
for(int j=1;j<=n*y;j++)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n*x;i++)
{
if(vis[a[i][j]]) return 0;
vis[a[i][j]]=1;
}
}
return 1;
}
void dfs(int x,int y,int cnt) //搜索
{
// cout<<x<<" "<<y<<" "<<cnt<<endl;
if(x==n+1)//结束条件
{
// cout<<check(n,n)<<endl;
if(check(n,n))
{
// cout<<1<<endl;
if(cnt<ans)//如果当前的代价小于答案的话更新
{
ans=cnt;
da=an;
}
}
return ;
}
if(check(x,y-1)==0) return ;
if(y==n+1) dfs(x+1,1,cnt);
for(int i=0;i<4;i++)
{
dfs(x,y+1,cnt+i);
change(x,y);
an.push(make_pair(x,y));
}
for(int i=0;i<4;++i) an.pop();
}
int main()
{
cin>>n;
for(int i=1;i<=n*n;i++)
{
for(int j=1;j<=n*n;j++)
{
cin>>c;
if(c>='0' and c<='9') a[i][j]=c-'0';
else a[i][j]=c-'A'+10;//进制转换
}
}
dfs(1,1,0);
int sz=0;
if(ans==0x3f3f3f3f) cout<<"-1\n";
else
{
cout<<ans<<endl;
pair<int,int>AA[N];//输出答案
while(da.size())
{
AA[++sz]=da.top();
da.pop();
}
for(int i=sz;i;i--) cout<<AA[i].first<<" "<<AA[i].second<<endl;
}
return 0;
}