传送门鸣人和佐助
深搜法:在图上深搜,需要剪枝
细节都在注释里了
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
int M,N,T;
char G[210][210];
int visited[210][210];
int minTime = 1 << 30; // 常用手段,便于一开始就能使用min函数
int curTime = 0;
int narutox,narutoy;
int sasukex,sasukey;
void Dfs(int x,int y);
int minT[210][210][20];//minT[i][j][k]表示剩余k查克拉 从起点到G[i][j]的最小时间
int DIRECTION[4][2] = {
{0,-1},
{0,1},
{-1,0},
{1,0}
};
int main()
{
cin >> M >> N >> T;
for(int i=0;i<M;++i)
{
for(int j=0;j<N;++j)
{
cin >> G[i][j];
if(G[i][j] == '@')
{
narutox = i;
narutoy = j;
}
else if(G[i][j] == '+')
{
sasukex = i;
sasukey = j;
}
for(int k=0;k<20;++k)
minT[i][j][k] = 1<<30;
}
}
memset(visited,0,sizeof(visited));
visited[narutox][narutoy] = 1;
Dfs(narutox,narutoy);
if(minTime < (1<<30))
cout << minTime << endl;
else
cout << "-1" << endl;
return 0;
}
void Dfs(int x,int y)
{
if(G[x][y] == '+')
{
minTime = min(minTime,curTime);
return ;
}
for(int i=0;i<4;++i)
{
int x1 = x+DIRECTION[i][0];
int y1 = y+DIRECTION[i][1];
if(x1 < 0 || x1 >= M || y1 < 0 || y1 >= N)
continue;
if(!visited[x1][y1])
{
//只可能是通路或大蛇丸手下
//剪枝
if(G[x1][y1] == '#' && T==0)
continue;
// if(curTime + 1 >= minTime) // 不好 因为只会剪掉 curTime=minTime-1
// continue;
if(curTime + 1+abs(sasukex-x1)+abs(sasukey-y1) > minTime)
continue;
if(G[x1][y1] == '#' && curTime+1 >= minT[x1][y1][T-1])
continue;
if(G[x1][y1] == '*' && curTime+1 >= minT[x1][y1][T])
continue;
//更新路径参数
if(G[x1][y1] == '#' && T>0)
--T;
curTime++;
minT[x1][y1][T] = curTime;
visited[x1][y1] = 1;
//cout <<"("<<x<<","<<y<<")" << "->" <<"("<<x1<<","<<y1<<")" <<endl;
Dfs(x1,y1);
//重置路径参数
curTime--;
visited[x1][y1] = 0;
if(G[x1][y1] == '#')
++T;
}
}
}
没有更好的剪枝方案时,如何进一步提升速度?
根据剪枝的特点,如果我们能很早的找到那个最优路径(最优解),那么我们就可以用它剪去更多的无用分枝。那么,如何才能更早的找到那个最优路径(最优解)呢?
首先,我们在进行深搜时,每次是沿着相邻节点去扩展路径的,本题中,每个节点可以往上下左右四个方向扩展。
那么,对于这么一种情况:
我们有理由认为(大概率),最优路径的方向是在右下角,因此我们可以优先让节点s的扩展方向为右、下,也就是重置方向偏移数组DIRECTION[4][2]={右偏移,下偏移,左偏移,上偏移}。
我们让每个节点在扩展的时候都有这么一个寻路偏好,那么深搜寻路时我们就有较大把握尽快找到那条最优路径,而不会去走很多多余的路
然而很多问题中这种寻路偏好没有本题这么好设定