中石油训练赛 - Bouldering(最短路+剪枝)

题目大意:给出一个 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108865283
今日推荐