简述题意:
算法:记忆化搜索+dp
难度:NOIP+
题解:
我们采用记忆化搜索的方法
设表示的是到达这个点,采用上述3种走法的第z种,已经更换了t次走法(第二个ans),已经按照顺序访问到了编号为k这个点时,所花费的最小代价。
然后,我们就可以快乐地去分类讨论,记忆化bfs了。
注意:我们记忆化搜索的时候,如果dp值已经被更新过,那么就不需要再次被更新了,想想这是为什么!
我们其实可以发现,队列中所有状态的dp值其实我们已经保证了单调递增,所以如果我们如果在此时刻已经对某一个状态进行了转移,那么下次如果还想要转移此状态,那么再次转移到此状态的dp值一定大于等于此状态dp已有的值!
最后统计答案时,要注意,第一次先找到最小的花费ans,第二次由于有可能有许多的t值满足答案为ans,这时我们选择第一层循环从小到大枚举t,这样,如果找到了与ans相等的dp值,即可输出这时的t,一定的最合法(小)的解。
伪代码如下:
int ans=0x3f3f3f3f;
for(int t = 0;t <= 205;t++)
{
for(int z = 1;z <= 3;z++)
{
if(dp[dex][dey][z][t][n*n]!=-1)
{
// cout << dp[dex][dey][z][t][n*n] << " ";
ans = min(dp[dex][dey][z][t][n*n],ans);
}
}
//puts("");
}
for(int t = 0;t <= 205;t++)//一定要先枚举t,因为我们已经统计出最小的ans,这时需要保证t最小!!!
{
for(int z = 1;z <= 3;z++)
{
if(dp[dex][dey][z][t][n*n]==ans)
{
//cout << t << " ";
printf("%d %d\n",ans,t);
exit(0);
}
}
}
注意:细节超级多!!!
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#define ll long long
#define N 15
using namespace std;
/*bfs搜索,状态可以这么设定dp[i][j][z][t][k]表示到达g[i][j]这个点,
用的是第z种走法,已经换了t次走法,已经按顺序访问了k个点,此时的最小精力值。*/
struct node
{
int x,y,k,t,z;
node(int x_x,int y_y,int k_k,int t_t,int z_z)
{
x=x_x;
y=y_y;
z=z_z;
k=k_k;
t=t_t;//刚刚把=打成-,莫名 MLE!!!w
}
};
int dp[12][12][5][N*N][N*N];
int dx1[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}}; //马
int dx2[4][2]={{-1,0},{1,0},{0,1},{0,-1}}; //车
int dx3[4][2]={{-1,-1},{-1,1},{1,-1},{1,1}}; //相
int mapp[N][N];
int n;
void bfs(int q,int w)
{
dp[q][w][1][0][1]=0;
dp[q][w][2][0][1]=0;
dp[q][w][3][0][1]=0;
queue<node>Q;
Q.push(node(q,w,1,0,1));
Q.push(node(q,w,1,0,2));
Q.push(node(q,w,1,0,3));
while(!Q.empty())
{
node u=Q.front();
Q.pop();
int x=u.x,y=u.y,z=u.z,k=u.k,t=u.t;
for(int i = 1;i <= 3;i++)
{
if(i==z) continue;
if(dp[x][y][i][t+1][k]!=-1) continue;
dp[x][y][i][t+1][k]=dp[x][y][z][t][k]+1;//刚刚把z打成了i????.....
Q.push(node(x,y,k,t+1,i));
}
if(z==1)
{
for(int i = 0;i < 8;i++)
{
int xx=x+dx1[i][0],yy=y+dx1[i][1],kk=k;
if(xx<1||xx>n||yy<1||yy>n) continue;
if(mapp[xx][yy]==k+1) kk++;
if(dp[xx][yy][z][t][kk]!=-1) continue;
dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
Q.push(node(xx,yy,kk,t,z));
}
}
if(z==2)
{
for(int j = 1;j <= 10;j++)
{
for(int i = 0;i < 4;i++)
{
int xx=x+j*dx2[i][0],yy=y+j*dx2[i][1],kk=k;
if(xx<1||xx>n||yy<1||yy>n) continue;
if(mapp[xx][yy]==k+1) kk++;
if(dp[xx][yy][z][t][kk]!=-1) continue;
dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
Q.push(node(xx,yy,kk,t,z));
}
}
}
if(z==3)
{
for(int j = 1;j <= 10;j++)
{
for(int i = 0;i < 4;i++)
{
int xx=x+j*dx3[i][0],yy=y+j*dx3[i][1],kk=k;
if(xx<1||xx>n||yy<1||yy>n) continue;
if(mapp[xx][yy]==k+1) kk++;
if(dp[xx][yy][z][t][kk]!=-1) continue;
dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
Q.push(node(xx,yy,kk,t,z));
}
}
}
}
}
int main()
{
//printf("%d\n",sizeof(dp)/1024/1024);
scanf("%d",&n);
int stx,sty,dex,dey;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
int x;
scanf("%d",&x);
mapp[i][j]=x;
if(x==1) stx=i,sty=j;
if(x==n*n) dex=i,dey=j;
}
}
memset(dp,-1,sizeof(dp));
bfs(stx,sty);
int ans=0x3f3f3f3f;
for(int t = 0;t <= 205;t++)
{
for(int z = 1;z <= 3;z++)
{
if(dp[dex][dey][z][t][n*n]!=-1)
{
// cout << dp[dex][dey][z][t][n*n] << " ";
ans = min(dp[dex][dey][z][t][n*n],ans);
}
}
//puts("");
}
for(int t = 0;t <= 205;t++)//一定要先枚举t,因为我们已经统计出最小的ans,这时需要保证t最小!!!
{
for(int z = 1;z <= 3;z++)
{
if(dp[dex][dey][z][t][n*n]==ans)
{
//cout << t << " ";
printf("%d %d\n",ans,t);
exit(0);
}
}
}
return 0 ;
}