做法
用向量乘积的方法判断点与直线的位置关系,先计算一侧的长度,再计算另一侧的长度即可。计算长度的时候可以用迪杰斯特拉算法。最后算结果的时候要将重复计算的终点和起点减去。
点与直线的位置关系判断
利用的是三角形面积的向量计算公式:
设三角形的三个点坐标分别是A(x1,y1),B(x2,y2),C(x3,y3)
则三角形的面积为 | (x1 - x3) * (y2 - y3) - (y1 - y3) * (x2 - x3) |
根据(x1 - x3) * (y2 - y3) - (y1 - y3) * (x2 - x3)的正负就可以判断点在直线的哪一侧了,如果是0的话说明在直线上。
代码
#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
const int N = 1e2 + 5;
const double f = 0.414213562373095; //√2-1
using namespace std;
struct point
{
int x, y;
} beg, des;
int pointjudge(point p1, point p2, point p3) //根据点与直线的位置关系返回0,1,2
{
int res;
res = (p1.x - p3.x) * (p2.y - p3.y) - (p1.y - p3.y) * (p2.x - p3.x);
if (res > 0) return 1;
if (res < 0) return 2;
return 0;
}
int score[N][N], pos[N][N], n, m;
bool vis[N][N];
double dis[N][N];
int dir1[2][4] = {{1, -1, 0, 0}, {0, 0, 1, -1}}; //上下左右方向
int dir2[2][4] = {{1, 1, -1, -1}, {1, -1, 1, -1}}; //斜对角方向
struct node
{
int x, y;
double dis;
friend bool operator<(node a, node b) { return a.dis > b.dis; }
};
void djsk()
{
dis[beg.y][beg.x] = 0;
priority_queue<node> q;
q.push(node{beg.x, beg.y, 0});
while (q.size())
{
node t = q.top();
q.pop();
if (t.y == des.y && t.x == des.x) return; //遇到终点退出
if (vis[t.y][t.x]) continue;
vis[t.y][t.x] = 1;
for (int i = 0; i < 4; i++) //上下左右
{
int xx = t.x + dir1[0][i];
if (xx < 0 || xx >= m) continue;
int yy = t.y + dir1[1][i];
if (yy < 0 || yy >= n) continue;
if (vis[yy][xx]) continue;
if (t.dis + score[yy][xx] < dis[yy][xx])
{
dis[yy][xx] = t.dis + score[yy][xx];
q.push(node{xx, yy, dis[yy][xx]});
}
}
for (int i = 0; i < 4; i++) //斜对角
{
int xx = t.x + dir2[0][i];
if (xx < 0 || xx >= m) continue;
int yy = t.y + dir2[1][i];
if (yy < 0 || yy >= n) continue;
if (vis[yy][xx]) continue;
if (t.dis + score[yy][xx] + f * (score[yy][xx] + score[t.y][t.x]) < dis[yy][xx])
{
dis[yy][xx] = t.dis + score[yy][xx] + f * (score[yy][xx] + score[t.y][t.x]);
q.push(node{xx, yy, dis[yy][xx]});
}
}
}
}
void init(int flag)
{
mem(vis, 0);
for (int i = 0; i < n; i++) //避免访问另一侧,将其vis置1
for (int j = 0; j < m; j++)
if (pos[i][j] == flag || pos[i][j] == 0) vis[i][j] = 1;
vis[beg.y][beg.x] = vis[des.y][des.x] = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
dis[i][j] = 1e10;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
double ans = 0;
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> score[i][j];
cin >> beg.x >> beg.y >> des.x >> des.y;
for (int i = 0; i < n; i++) //计算每个点与直线的位置关系
for (int j = 0; j < m; j++)
pos[i][j] = pointjudge(beg, des, point{j, i});
//先处理直线一侧
init(1);
djsk();
ans += dis[des.y][des.x];
//再处理直线另一侧
init(2);
djsk();
ans += dis[des.y][des.x];
ans -= score[des.y][des.x]; //减去重复计算的终点
ans += score[beg.y][beg.x]; //加上没有计算的起点
cout << fixed << setprecision(2) << ans << endl; //输出保留两位小数
return 0;
}