Description
给定一个一定存在从起点到终点的路径的四联通迷宫。已知Tar左右方向移动的时间为1,上下移动的时间为未知实数v。求当Tar从起点到终点的最短移动时间为已知实数L时,未知实数v是多少。
输入数据包含多个测试点。第一行为一个整数T,表示测试点的数目。
对于每一个测试点,第一行包含实数L和两个整数R,C。R为迷宫的上下长度,C为迷宫的左右长度。
之后的R行,每行包含C个字符。其中空格表示空地,S表示起点,E表示终点,#表示围墙。
Output
对于每一个测试点,在单独的一行内输出未知实数v,输出保留5位小数。
2
2.5 4 5
#####
#S #
# E#
#####
21 13 12
############
#S## #E#
# ## # # #
# # # # #
### # # # #
# # # # #
# ## # # #
## # # # #
### # # # #
## # # # #
# ## # #
# # #
############
Sample Output
0.50000
0.21053
Data Constraint
20%的数据,1≤ R,C ≤ 10。
100%的数据,1≤ R,C ≤ 100,0≤ v <10。
解题思路
因为上下移动时间不知道,而这个又影响最短时间的长度,贪心不可用。
转换一下思维,若已知上下移动时间,则最短时间时间十分好求,于是二分上下移动时间v,以这个时间为已知条件跑最短路,若比最短时间还大,缩小v值,若比最短时间小,增大v值。
主要处理小数精度问题。
using namespace std;
int go[4][3]={{1,0,0},{0,1,1},{-1,0,0},{0,-1,1}};
int a[N][N];
char s[N];
int s1,s2,e1,e2;
int r,c,ti;
double l,v;
double dis[N][N];
void init()
{
scanf("%lf%d%d\n",&l,&r,&c);
memset(a,0,sizeof(a));
for (int i=1;i<=r;++i)
{
gets(s);
for (int j=1;j<=c;++j)
{
if (s[j]=='#') a[i][j]=1;
else a[i][j]=0;
if (s[j]=='S') {s1=i; s2=j;}
if (s[j]=='E') {e1=i; e2=j;}
}
}
}
int q[N*N][2];
int head,tail;
double t[2];
double bfs(double v)
{
t[0]=v;
t[1]=1.00;
for (int i=1;i<=r;++i)
for (int j=1;j<=c;++j)
dis[i][j]=l+1;
memset(q,0,sizeof(q));
head=0; tail=1;
q[1][0]=s1;
q[1][1]=s2;
dis[s1][s2]=0;
while (head<=tail)
{
head++;
int i=q[head][0];
int j=q[head][1];
for (int k=0;k<=3;++k)
{
int x,y;
x=i+go[k][0];
y=j+go[k][1];
if (x>r || x<1 || y>c || y<1 || a[x][y]==1) continue;
if (dis[i][j]+t[go[k][2]]<dis[x][y])
{
dis[x][y]=dis[i][j]+t[go[k][2]];
tail++;
q[tail][0]=x;
q[tail][1]=y;
}
}
}
return dis[e1][e2];
}
void find()
{
double le,ri,mid,ans;
le=0.00;
ri=l;
while (le<ri)
{
mid=(le+ri)/2;
ans=bfs(mid);
//cout<<mid<<' '<<ans<<' '<<le<<' '<<ri<<' '<<l<<endl;
if (ans-l==0) {le=mid; ri=mid; break;}
if (ans<l) le=mid+0.0000001;
else ri=mid-0.0000001;
}
printf("%0.5lf\n",mid);
}
int main()
{
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d",&ti);
for (int p=1;p<=ti;++p)
{
init();
find();
}
return 0;
}