Atcoder 2024-04-06比赛 D - Medicines on Grid题解

一.题目描述

二.题目翻译+思路分析

1.题目翻译:

给定一个起点S和终点T,"."为通路,“#”为墙,以此形成一个矩阵图,下面再给定对应坐标所拥有的能量值。从起点到终点每走一步(横向或竖向移动)消耗一点能量值,若走到有能量值的点则可以选择用该点处能量值刷新能量值或凭借当前能量值继续移动。如果可以实现从S移动到T则输出“Yes”,否则输出“No”。

2.思路:

一开始以为是图论最短路问题,用spfa搓了一下,但是发现最短路如果能量不够也白搭。

更换思路:利用深搜并结合一下动态规划的思想。以能量值为核心不断更新搜到点的能量值,即走到当前点位剩余能量值如果可以更多则更新能量值。如果当前点更新过(能量值更优),将该点push进队列继续搜索。如果搜索到终点,能量值>=0。那么成功,反之失败。

思路没多难,难的是各种小细节别出错(短时间内敲出精准的代码)

还要审题清楚,灵活变通(我为审题不清者哀哭,wa雨,终将落下

3.代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int inf=1e9+8;
const int N=2e6+7;
map<int,int>drink;//存储能量
int sam=0; //当前能量
int s,t;   //起点和终点
int x,y;   //图的列和行
/*
存图,u为起点,v为u的连接点,w为边权,为1
*/
struct edge{
    int v,w;
    edge(int _v=0,int _w=0):v(_v),w(_w){}
};
vector<edge> e[N];
void add_edge(int u,int v,int w){
    e[u].push_back(edge(v,w));
}
int vis[N];//存储当前点最优能量值
int n,m; 
int spfa(int s) //搜索主函数
{  
	sam=drink[s]; //更新起点(起点必须有能量,否则根本无法移动)
	vis[s]=sam;   //更新最优能量
	if(sam==0) {return 1;} //起点没能量,寄了
   	queue<int>q;  
    q.push(s);   //起点进队
    while(!q.empty()){
        int u=q.front(); //队首点出队,准备以此点进行深搜
        q.pop();    
		sam=vis[u];      //sam更新为最优能量
		if(sam==0) {continue;} //当前点能量没了,停止搜索该点,换另一个点
        for(int i=0;i<(int)e[u].size();++i){
            int v=e[u][i].v;   //依次取出该点所有临点
            sam=vis[u];		
			if(drink[v]>sam-1)
			{
				sam=drink[v];  //当前点有能量可供替换,且该点能量大于当前剩余能量
			}
			else if(sam==0)
			{
				continue;		//当前能量为0,停止搜索。
			}
			else {
				sam--;			//当前能量-1
			}
			if(v==t&&sam>=0){return 0;}	//搜索到了
			if(sam>vis[v]){
				q.push(v);     //更新最优能量值,同时将该点push进队
				vis[v]=sam;
			}
		}
    }   
	return 1; //没搜到
}

signed main(){
   	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin>>x>>y;
	map<int ,char>cc;
	/*
	化二维为一维存图
	eg: 1 2  3  4
		5 6  7  8
		9 10 11 12
		一个点可以连接上下左右四个点(用map存可以避免讨论负值的麻烦)
	*/
	for(int i=1;i<=x;i++){
		for(int j=1;j<=y;j++){
			char o;
			cin>>o;
			cc[j+(i-1)*x]=o;
			if(o=='S')s=j+(i-1)*x;
			if(o=='T')t=j+(i-1)*x;
			if(o=='.'||o=='S'||o=='T'){
				if((cc[j+(i-1)*x-1]=='.'||cc[j+(i-1)*x-1]=='S'||cc[j+(i-1)*x-1]=='T')&&(j+(i-1)*x-1)%y!=0){//(j+(i-1)*x-1)%y!=0这个条件是判断防止4和5连上这种情况(上面注释打的图例)
					add_edge(j+(i-1)*x,j+(i-1)*x-1,1);
					add_edge(j+(i-1)*x-1,j+(i-1)*x,1);
				}
				if((cc[j+(i-1)*x+1]=='.'||cc[j+(i-1)*x+1]=='S'||cc[j+(i-1)*x+1]=='T')&&(j+(i-1)*x+1)%y!=1){//(j+(i-1)*x+1)%y!=1这个条件是判断防止5和4连上这种情况(上面注释打的图例)
					add_edge(j+(i-1)*x,j+(i-1)*x+1,1);
					add_edge(j+(i-1)*x+1,j+(i-1)*x,1);
				}
				if(cc[j+(i-2)*x]=='.'||cc[j+(i-2)*x]=='S'||cc[j+(i-2)*x]=='T'){
					add_edge(j+(i-1)*x,j+(i-2)*x,1);
					add_edge(j+(i-2)*x,j+(i-1)*x,1);
				}
				if(cc[j+i*x]=='.'||cc[j+i*x]=='S'||cc[j+i*x]=='T'){
					add_edge(j+i*x,j+(i-1)*x,1);
					add_edge(j+(i-1)*x,j+i*x,1);
				}
			}
		}
	}
	int n;
	cin>>n;
	int a,b,c;
	for(int i=1;i<=n;i++){
		cin>>a>>b>>c;
		drink[(a-1)*x+b]=c; //存能量点
	}
    int ans=spfa(s);
	if(ans==1)cout<<"No";
	else cout<<"Yes";
    return 0;
}

4.总结

蒟蒻改了好久才改对,但是自己AC的一刻很有成就感!写一篇博客纪念自己的足迹。

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值