CF1520G To Go Or Not To Go? 反向bfs,最短路,一

cf链接:Problem - G - Codeforces

学到的东西:

1.用dis来记录从某起点到某一个点的最小步数,方便许多

2.用queue<sturct>q这样的形式来定义队列简单方便

3.数据范围的问题:INF最好用0X3F3F3F3F3F3F3F(七个3f)。因为:1.足够大,2.十个INF加起来不会爆longlong(这个地方让我改了五次,吐血,150个测试点跑半天)

4.一些小细节和技巧

 

输入输出样例

输入 #1

5 5 1
0 -1 0 1 -1
0 20 0 0 -1
-1 -1 -1 -1 -1
3 0 0 0 0
-1 0 0 0 0

输出 #1

14

一道思维题:只能用一次传送门。为什么呢?下面是证明:

如果用多次传送门xy -> pq ......mn ->rs ,不如直接xy ->rs,明显更划算。

但是可能有一次都不用传送门的情况,直接走到终点。所以就分两种情况考虑:用一次传送门或者用0次传送门(直接走过去)。然后就是一些小细节了,详情看代码。

Talk is cheap.Show me the code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f
#define endl '\n'
#define MOD 998244353
int n,m,w;
int a[2005][2005];
int dis[2005][2005];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
int anss=INF;

int bfs(int sx,int sy){
	memset(dis,-1,sizeof(dis));
	dis[sx][sy]=0;
	int ans=INF;
	queue<pair<int,int>>q;
	q.push({sx,sy});
	while(!q.empty()){
		int x=q.front().first,y=q.front().second;
		q.pop();
		for(int i=0;i<4;i++){
			int xx=x+dx[i];
			int yy=y+dy[i];
			if(xx<1||xx>n||yy<1||yy>m||a[xx][yy]==-1||dis[xx][yy]!=-1)	continue;
			q.push({xx,yy});
			dis[xx][yy]=dis[x][y]+1;
		}
	}
	if(sx==1&&dis[n][m]!=-1)	anss=min(anss,dis[n][m]*w);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(a[i][j]>0&&dis[i][j]!=-1){
					ans=min(ans,dis[i][j]*w+a[i][j]);
				}
			}
		}
		return ans;
}

void solve(){
	cin>>n>>m>>w;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		cin>>a[i][j];
		}
	}
    int ans1=bfs(1,1);
    int ans2=bfs(n,m);
    anss=min(anss,ans1+ans2);
    if(anss>=INF)	cout<<-1<<endl;
    else			cout<<anss<<endl;
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
	solve();
}


//考虑边界了?
//考虑特殊情况?
//考虑输出中间值勘误?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值