天梯赛 森森美图

https://pintia.cn/problem-sets/994805046380707840/problems/994805047395729408

先说一下题意

在一个二维点阵上选两个点s、t,这两个点所在的直线将这个点阵分成两部分

在这两部分里面分别找一条s到t的路径(八连通),这两条路径构成一个闭环,最小化闭环的分数

注意直线所经过的点不属于任何一部分,不能使用

闭环的分数定义为:

每个点都有一个分数,将闭环上的点的分数相加

同时,如果两个点是斜着连通的,那还需要额外加上这两个点分数和的(根号2-1)倍

踩的坑:

1、点的坐标是从0开始计算的

2、向右为x轴,向下为y轴,即给出的点的坐标第一个是列坐标,第二个是行坐标

3、如果路径连续斜着经过的点有3个或更多个,是所有斜着相邻连通的点对都有额外的加分,也就是中间斜着的会计算2次额外加分

这就是在每一个部分做一次spfa

如何判断下一个点v是否在该部分:利用叉积

x到s看作是一个向量,x到t看作是一个向量

两个向量做叉积

所有叉积<0的是一部分,所有叉积>0的是一部分,所有叉积=0的不属于任何一部分

#include<queue>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 101
 
int n,m; 
int mp[N][N];

int sx,sy,tx,ty;
double pp;

struct node
{
    int x,y;
};

int dx[8]={-1,1,0,0,-1,-1,1,1}; //上、下、左、右、左上、右上、左下、右下 
int dy[8]={0,0,-1,1,-1,1,-1,1};

double d[N][N];
bool vis[N][N];

int tty; 
double ans;

bool judge(int x,int y) //叉积判断是否属于同一部分 
{
    int a1=sx-x,b1=sy-y;
    int a2=tx-x,b2=ty-y;
    if(tty==1) return (a1*b2-b1*a2<0);
    else return (a1*b2-b1*a2>0);
}

void bfs()
{
    pp=sqrt(2)-1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            d[i][j]=2e9; 
    queue<node>q;
    node now,nxt;
    now.x=sx;
    now.y=sy;
    d[sx][sy]=mp[sx][sy];
    vis[sx][sy]=true;
    q.push(now);
    int nx,ny;
    double nd;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        vis[now.x][now.y]=false;
        for(int i=0;i<8;++i)
        {
            nx=now.x+dx[i];
            ny=now.y+dy[i];
            if(nx<1 || nx>n || ny<1 || ny>m) continue;
            if(!judge(nx,ny) && (nx!=tx || ny!=ty)) continue;
            nd=d[now.x][now.y]+mp[nx][ny];
            if(i>=4) nd+=(mp[nx][ny]+mp[now.x][now.y])*pp; //斜着额外的分数 
            if(nd<d[nx][ny])
            {
                d[nx][ny]=nd;
                if(nx==tx && ny==ty) continue;
                if(!vis[nx][ny])
                {
                    nxt.x=nx;
                    nxt.y=ny;
                    q.push(nxt);
                    vis[nx][ny]=true;
                }
            }
        }
    }
    ans+=d[tx][ty]; 
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j) 
            scanf("%d",&mp[i][j]);
    scanf("%d%d%d%d",&sy,&sx,&ty,&tx);
    sy++;
    sx++;
    ty++;
    tx++;
    tty=1; //标记现在是做哪一部分
    bfs();
    tty=2;//标记现在是做哪一部分
    bfs();
    printf("%.2lf",ans-mp[sx][sy]-mp[tx][ty]); //起始位置算了2次 
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/13769969.html