题目大意:给出一个 n * m 的矩阵,矩阵中有些许数字,两个数字之间的距离如果小于 r 的话就是可达,到达一个数字后会消耗数字对应的体力值,问从最下面的数字到最上面的数字的最短路是多少,必须要保证体力值不能为负
题目分析:很容易写出一个分层图最短路,严格来说就是个二维最短路,d[ x ][ s ] 代表从起点到达点 x 时,消耗了 s 点体力的最短路,不过会 TLE,考虑剪枝
因为在这个题目中最短路的优先级最高,换句话说,在相同的一个点,肯定距离越近且体力消耗越少越好,如果当前 y 位置下,最短距离为 dis,且体力为 s,如果从 y 位置转移过来得到的距离 dis' 和 s' ,有关系 dis' > dis && s' > s,显然这个状态一定不会为最后的最后状态做出贡献,直接减掉即可
代码写的有点丑,但还是贴上来吧
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
const double eps=1e-8;
int sgn(double x)
{
if(fabs(x)<=eps)
return 0;
if(x<0)
return -1;
else
return 1;
}
struct Node
{
int x,y,val;
Node(int x,int y,int val):x(x),y(y),val(val){}
bool operator<(const Node& t)const
{
return x<t.x;
}
};
struct Edge
{
int to,next;
double w;
}edge[30*30*30*30];
char maze[30][30];
vector<Node>node;
int head[30*30],cnt,sum;
double d[30*30][25*25*10+100];
pair<double,int>mmin[30*30];
bool vis[30*30][25*25*10+100];
void addedge(int u,int v,double w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
struct Node2
{
double dis;
int to,s;
Node2(int to,double dis,int s):to(to),dis(dis),s(s){}
bool operator<(const Node2& t)const
{
if(sgn(dis-t.dis)!=0)
return sgn(dis-t.dis)>0;
else
return s>t.s;
}
};
void Dijkstra(int st)
{
for(int i=0;i<=node.size();i++)
{
mmin[i]=make_pair(1e10,-1);
for(int j=0;j<=sum;j++)
{
d[i][j]=1e10;
vis[i][j]=false;
}
}
priority_queue<Node2>q;
int s=node[st].val;
q.push(Node2(st,0,s));
d[st][s]=0;
while(q.size())
{
Node2 cur=q.top();
q.pop();
int u=cur.to,s=cur.s;
if(vis[u][s])
continue;
vis[u][s]=true;
if(sgn(mmin[u].first-d[u][s])>0)//剪枝
mmin[u]=make_pair(d[u][s],s);
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
double w=edge[i].w;
int ss=node[to].val;
if(s+ss>sum)
continue;
if(sgn(d[u][s]+w-mmin[to].first)>=0&&s+ss>=mmin[to].second)
continue;
if(d[to][s+ss]>d[u][s]+w)
{
d[to][s+ss]=d[u][s]+w;
q.push(Node2(to,w+cur.dis,s+ss));
}
}
}
}
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
int n,m,r,s;
scanf("%d%d%d%d",&n,&m,&r,&s);
for(int i=1;i<=n;i++)
scanf("%s",maze[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(maze[i][j]=='.')
continue;
node.push_back(Node(i,j,maze[i][j]-'0'));
sum+=maze[i][j]-'0';
}
sum=min(sum,s);
int st=0,ed=node.size()-1;
for(int i=0;i<node.size();i++)
for(int j=i+1;j<node.size();j++)
{
double dis=hypot(node[i].x-node[j].x,node[i].y-node[j].y);
if(sgn(dis-r)>0)
continue;
addedge(i,j,dis);
addedge(j,i,dis);
}
Dijkstra(st);
double ans=1e10;
for(int i=0;i<=sum;i++)
ans=min(ans,d[ed][i]);
if(sgn(ans-1e10)==0)
puts("impossible");
else
printf("%.15f\n",ans);
return 0;
}