L3-018 森森美图 (30 分)

15 篇文章 1 订阅
10 篇文章 0 订阅

题目

题目链接

题解

BFS。


先看看样例咋出来的吧。

在这里插入图片描述

判断某个坐标属于起点终点连线的哪一侧的时候,我们采用是将点代入起点终点的两点式中根据正负值判断,两次bfs更新起点到终点的“距离”。

bfs每次扩展一个点,用起点到该点的“距离”更新其八个方向上的点的“距离”,如果八个方向上的点保存的“距离”被更新了,则入队,可以用这些点继续更新别的点,否则不要入队了,因为别的点已经由这些点更新过了,再加入个没变的“距离”还是不会有任何效果的,所以直接不入队,节约时间。

坑点:

  1. 注意不要重复统计起点和终点。(这个点因为我瞎眼,卡了也就将近俩小时吧)
  2. 注意x是轴是水平方向,y轴是竖直方向,所以列的变化方向为x轴变化方向,行的变化方向为y轴变化方向。我采用的方法是转置,因为还是觉得常见的保存方式做起来比较顺手(顺手:指debug两个半小时)

补充几点:

已知三角形三个顶点,计算三角形面积可以使用行列式:

在这里插入图片描述

当然,加上绝对值才算面积,也可以计算这个行列式的正负来判断属于直线的不同侧,展开再合并可以得到两点式的变形。

(实在懒得写Latex了,就截了个)


下面的程序中注释掉的代码部分为输出路径的语句,如果不理解可以输出看看。


我他妈服了,ljPTA题目描述真恶心,废话多而且描述的还不清楚,限制还贼多。


最后,感谢大佬的博客,如果不是大佬的博客,我可能永远都不知道样例是咋算出来的。

代码

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

const double C = sqrt(2) - 1, MAX_DOUBLE = 1e50;
const int N = 110;

int n, m, flag;
int stx, tax, sty, tay;
//PII path[N][N];
double a[N][N], w[N][N];

double ans;

bool check (int x, int y) {
	// 判断是否为直线的flag一侧
	// 当(x,y)为终点时也要特殊判断返回的是可行,即属于flag一侧 
	double area =  (tax-stx) * (y-sty) - (tay-sty) * (x-stx);
	if (flag * area > 0 || (x == tax && y == tay)) return true;
	return false; 
}

void bfs () {
	// 初始化 
	for (int i = 0;i < n;i ++)
	for (int j = 0;j < m;j ++)
	w[i][j] = MAX_DOUBLE;
	
	queue <PII> q;
	q.push ({stx, sty});
	w[stx][sty] = a[stx][sty];
	
	while (q.size()) {
		PII t = q.front ();
		int x = t.first, y = t.second;
		q.pop ();
		
		for (int i = -1;i <= 1;i ++) // 枚举八个方向 
			for (int j = -1;j <= 1;j ++) {
				if (i == 0 && j == 0) continue; // 是(x,y)则continue 
				int tx = x+i, ty = y+j;
				if (tx < 0 || ty < 0 || tx >= n || ty >= m) continue; // 越界 
				if (!check (tx, ty)) continue; // 如果是终点或不是直线上的点则不会被continue掉 
				double tw = w[x][y] + a[tx][ty] + ((abs(i) && abs(j)) * C * (a[x][y] + a[tx][ty]));
				// 当为斜线时要加上额外的值 
				if (w[tx][ty] > tw) {
					if (tx != tax || ty != tay) q.push ({tx, ty});
					w[tx][ty] = tw;
//					path[tx][ty] = {x, y}; // 保存路径 
				}
			}
	}
}

//void printpath (int x, int y) { // 输出路径函数 
//	if (x == -1 || y == -1) return ;
//	printpath (path[x][y].first, path[x][y].second);
//	cout << x << ' ' << y << ' ' << a[x][y] << endl;
//}

int main()
{
	cin >> n >> m;
	for (int i = 0;i < n;i ++)
	for (int j = 0;j < m;j ++)
	cin >> a[i][j];

	// 转置一下 
	swap (n, m);
	for (int i = 0;i < max(n, m);i ++)
	for (int j = i+1;j < max(n, m);j ++)
	swap (a[i][j], a[j][i]);
	
	cin >> stx >> sty >> tax >> tay;  
	
	// 注释部分为路径输出 
//	memset (path, -1, sizeof path);
	flag = -1; bfs (); ans += w[tax][tay]; // 因为通过公式计算得到的值可能为正值或负值,通过flag控制属于直线的哪一侧 
//	cout << "----------" << endl; printpath (tax, tay); 
//	memset (path, -1, sizeof path);
	flag = 1; bfs (); ans += w[tax][tay];
//	cout << "----------" << endl; printpath (tax, tay);

	printf ("%.2lf", ans - a[stx][sty] - a[tax][tay]); // 一定要减去重复统计的起点和终点! 

	return 0;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值