记录一个Heisenbug!

记录一个错误的正确!!!
我觉得归功于编译器
我要学编译原理!!!


问题是这样的
传送门:
https://www.luogu.org/problemnew/show/P4009
给定一个 N \times NN×N 的方形网格,设其左上角为起点◎,坐标(1,1)(1,1),XX 轴向右为正, YY 轴向下为正,每个方格边长为 1。

一辆汽车从起点◎出发驶向右下角终点▲,其坐标为 (N,N)(N,N)。

在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

汽车只能沿网格边行驶,装满油后能行驶 KK 条网格边。出发时汽车已装满油,在起点与终点处不设油库。

汽车经过一条网格边时,若其 XX 坐标或 YY 坐标减小,则应付费用 BB ,否则免付费用。

汽车在行驶过程中遇油库则应加满油并付加油费用 AA。

在需要时可在网格点处增设油库,并付增设油库费用 CC(不含加油费用AA )。

N,K,A,B,CN,K,A,B,C 均为正整数, 且满足约束: 2\leq N\leq 100,2 \leq K \leq 102≤N≤100,2≤K≤10。

设计一个算法,求出汽车从起点出发到达终点所付的最小费用。


这是一个我不是很会的题
后来了解到用BFS暴力可解

扫描二维码关注公众号,回复: 10972148 查看本文章

然后写了朴素的暴力广搜

#include<stdio.h>

typedef struct{
	int x;
	int y;
	int fee;
	int oil;
}Status; 

int dir[4][3] = {{-1,0,0},{0,-1,0},{1,0,0},{0,1,0}};//←,↑,→,↓ 

int main(){
	int N,K,A,B,C;
	scanf("%d%d%d%d%d",&N,&K,&A,&B,&C);
	dir[0][2] =  dir[1][2] = B;//回退费用 
	char map[105][105]={0};
	for(int i = 1;i<=N;i++){
		for(int j = 1;j<=N;j++){
			scanf("%d",&map[i][j]);
		}
	}
	
	Status status[100000];
	int left = 0, right = 0;
	int tx, ty, tfee;
	int minFee = 0x3f3f3f3f;
	status[right++] = {1,1,0,K};//起始位置 
	
	while(left<=right){//BFS
		Status t = status[left++];
		if(t.x == N || t.y == N)continue;
		for(int i = 0;i<4;i++){
			tx = t.x+dir[i][0];
			ty = t.y+dir[i][1];
			if(tx == N && ty == N){//到达终点 
				minFee = t.fee>minFee?minFee:t.fee;
				break;
			}
			if(tx<1||tx>N||ty<1||ty>N)continue;
			status[right].x = tx;
			status[right].y = ty;
			status[right].oil = t.oil - 1;
			tfee = t.fee+dir[i][2];//方向收费
			if(map[tx][ty]==1){//加油站 
				tfee += A;
				status[right].oil = K; 
			}else if(t.oil==1){//半路没油 前面保证(tx,ty)没到终点 
				tfee += C+A;
				status[right].oil = K;
			}
			status[right++].fee = tfee;
		}
	}
	printf("%d\n",minFee);
	return 0;
} 

可以看到这里我忘记判断有没有形成环路

然而我在DEVC++中运行 居然可以得到正确结果!
稳定地得到正确结果
在这里插入图片描述
惊讶!一直不太相信的DEV环境居然能把这种优化掉

估计是爆数组后,程序就停下了。刚好最优结果存在。

我在VS里一直是死循环。。。
想半天没有想通为啥死循环,调试了才知道。。
I’m Vegetable. : (


2019/4/23


甚至第一版代码中
while(left<=right){}中
第一个if循环判断条件
if(t.x == N || t.y == N)
都写错了 应该是&&的
真不知道是怎么出正确结果的 摊手


更新正确答案

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

typedef struct {
	int x;
	int y;
	int fee;
	int oil;
}Status;

int dir[4][3] = { {-1,0,0},{0,-1,0},{1,0,0},{0,1,0} };//←,↑,→,↓ 

int main() {
	int N = 0, K = 0, A = 0, B = 0, C = 0;
	scanf("%d%d%d%d%d", &N, &K, &A, &B, &C);
	dir[0][2] = dir[1][2] = B;//回退费用 
	char map[105][105] = { 0 };
	int mapOilFee[105][105][15];//一二维x, y, 第三维下标oil 值fee 记录较好状态下的油量与对应的费用 防止死循环
	memset(mapOilFee, 0x3f, sizeof(mapOilFee));
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			scanf("%d", &map[i][j]);
		}
	}

	queue<Status> status;
	int tx, ty, tfee;
	int minFee = 0x3f3f3f3f;
	Status t = { 1,1,0,K };
	mapOilFee[1][1][K] = 0;//油量为K时fee==0
	status.push(t);

	while (status.size() != 0) {//BFS
		t = status.front();
		status.pop();
		if (t.x == N && t.y == N)continue;
		for (int i = 0; i < 4; i++) {
			tx = t.x + dir[i][0];
			ty = t.y + dir[i][1];
			if (tx == N && ty == N) {//到达终点 
				minFee = t.fee > minFee ? minFee : t.fee;
				break;
			}
			if (tx<1 || tx>N || ty<1 || ty>N)continue;
			Status tem = { tx,ty,0,t.oil - 1 };
			tfee = t.fee + dir[i][2];//方向收费
			if (map[tx][ty] == 1) {//加油站 
				tfee += A;
				tem.oil = K;
			}
			else if (t.oil == 1) {//半路没油 前面保证(tx,ty)没到终点 
				tfee += C + A;
				tem.oil = K;
			}
			tem.fee = tfee;
			if (tfee < mapOilFee[tx][ty][tem.oil]) {
				status.push(tem);
				mapOilFee[tx][ty][tem.oil] = tfee;
			}
		}
	}
	printf("%d", minFee);
	return 0;
}

BFS暴力搜索每个状态
用队列优化空间复杂度
判断一定得不到最优解的提前结束

优化不够 会超时 会超时 会超时


2019/4/24

发布了85 篇原创文章 · 获赞 83 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/kafmws/article/details/89483008