工欲善其事,必先利其器。——《论语》
题目
给你一个大小为 3 * 3
,下标从 0 开始的二维整数矩阵 grid
,分别表示每一个格子里石头的数目。网格图中总共恰好有 9
个石头,一个格子里可能会有 多个 石头。
每一次操作中,你可以将一个石头从它当前所在格子移动到一个至少有一条公共边的相邻格子。
请你返回每个格子恰好有一个石头的 最少移动次数 。
难度:中等
分析
今天新学到了一个有用的库函数,必须记录一下✊。此题的最终的目的是让每一个格子里有一块石头,因此不需要动只有一块石头的格子,只要把格子中多出来的石头移动到没有石头的格子中。我们可以使用两个数组记录石头移动出的位置和移动到的位置,为了使其数量对应,一个格子每多出一块石头,就将该位置添加到数组中一次。最终我们得到的是两个长度相等的数组,每个移出位置与一个移入位置对应,计算移动步数求和即可得到总步数。此题似乎没有贪心方法,由于数据量较小,枚举即可。我们只需要固定一个数组,求另一个数组的全排列,分别计算出答案取最小值即可。对于此类枚举,我只会回溯😔,不过在C++<algorithm>库的next_permutation函数可以方便地求出全排列(下一个排列,prev_permutation上一个排列,注意:如果要获得全排列,数组初始需要按升序排序),推荐使用。因为石头移出位置的数组有重复,求该数组的全排列时间更少。
解答
回溯
class Solution {
public:
int minimumMoves(vector<vector<int>>& grid) {
vector<vector<int>> send,receive;
int n=grid.size();
for (int i=0;i<n;i++){
for (int j=0;j<n;j++){
if (grid[i][j]==0){
receive.push_back({i,j});
}else if (grid[i][j]>1){
for (int _=0;_<grid[i][j]-1;_++){
send.push_back({i,j});
}
}
}
}
int ans=INT_MAX;
vector<int> visited(receive.size());
dfs(ans,send,receive,visited,0,0);
return ans;
}
int step(vector<int>& v1, vector<int>& v2){
return abs(v1[0]-v2[0])+abs(v1[1]-v2[1]);
}
void dfs(int& ans,vector<vector<int>>& send, vector<vector<int>>& receive, vector<int>& visited, int index,int sum){
if (index==send.size()){
ans=min(ans,sum);
return;
}
for (int j=0;j<receive.size();j++){
if (visited[j]==0){
visited[j]=1;
dfs(ans,send,receive,visited,index+1,sum+step(send[index],receive[j]));
visited[j]=0;
}
}
}
};
库函数
class Solution {
public:
int minimumMoves(vector<vector<int>>& grid) {
vector<vector<int>> send,receive;
int n=grid.size();
for (int i=0;i<n;i++){
for (int j=0;j<n;j++){
if (grid[i][j]==0){
receive.push_back({i,j});
}else if (grid[i][j]>1){
for (int _=0;_<grid[i][j]-1;_++){
send.push_back({i,j});
}
}
}
}
int ans=INT_MAX;
do{ //全排列
int sum=0;
for (int i=0;i<send.size();i++){
sum+=step(send[i],receive[i]);
}
ans=min(ans,sum);
}while(next_permutation(send.begin(),send.end()));
return ans;
}
int step(vector<int>& v1, vector<int>& v2){
return abs(v1[0]-v2[0])+abs(v1[1]-v2[1]);
}
};