PTA 森森美图 (30 分)

本文介绍了森森开发的一款照片美化软件,通过计算像素点与周围点的相似程度来识别面部轮廓。用户选择两个像素点,软件会寻找两条与轮廓最匹配的曲线来定义面部区域。问题转化为寻找得分最高的曲线,涉及到八连通像素和特定得分计算。输入和输出格式以及样例案例都已给出,解决方法包括使用两次广度优先搜索(BFS)并判断叉乘。

7-15 森森美图 (30 分)
森森最近想让自己的朋友圈熠熠生辉,所以他决定自己写个美化照片的软件,并起名为森森美图。众所周知,在合照中美化自己的面部而不美化合照者的面部是让自己占据朋友圈高点的绝好方法,因此森森美图里当然得有这个功能。 这个功能的第一步是将自己的面部选中。森森首先计算出了一个图像中所有像素点与周围点的相似程度的分数,分数越低表示某个像素点越“像”一个轮廓边缘上的点。 森森认为,任意连续像素点的得分之和越低,表示它们组成的曲线和轮廓边缘的重合程度越高。为了选择出一个完整的面部,森森决定让用户选择面部上的两个像素点A和B,则连接这两个点的直线就将图像分为两部分,然后在这两部分中分别寻找一条从A到B且与轮廓重合程度最高的曲线,就可以拼出用户的面部了。 然而森森计算出来得分矩阵后,突然发现自己不知道怎么找到这两条曲线了,你能帮森森当上朋友圈的小王子吗?

为了解题方便,我们做出以下补充说明:

图像的左上角是坐标原点(0,0),我们假设所有像素按矩阵格式排列,其坐标均为非负整数(即横轴向右为正,纵轴向下为正)。
忽略正好位于连接A和B的直线(注意不是线段)上的像素点,即不认为这部分像素点在任何一个划分部分上,因此曲线也不能经过这部分像素点。
曲线是八连通的(即任一像素点可与其周围的8个像素连通),但为了计算准确,某像素连接对角相邻的斜向像素时,得分额外增加两个像素分数和的√
​2

​​ 倍减一。例如样例中,经过坐标为(3,1)和(4,2)的两个像素点的曲线,其得分应该是这两个像素点的分数和(2+2),再加上额外的(2+2)乘以(√
​2

​​ −1),即约为5.66。
输入格式:
输入在第一行给出两个正整数N和M(5≤N,M≤100),表示像素得分矩阵的行数和列数。

接下来N行,每行M个不大于1000的非负整数,即为像素点的分值。

最后一行给出用户选择的起始和结束像素点的坐标(X
​start
​​ ,Y
​start
​​ )和(X
​end
​​ ,Y
​end
​​ )。4个整数用空格分隔。

输出格式:
在一行中输出划分图片后找到的轮廓曲线的得分和,保留小数点后两位。注意起点和终点的得分不要重复计算。

输入样例:
6 6
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
2 1 5 4
输出样例:
27.04
正反做两次bfs,判一下叉乘

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,vis[N][N],sx,sy,ex,ey;
double s[N][N],d[N][N],mi;
const double PI=sqrt(2)-1;
int dir[8][2]= {1,0,-1,0,0,1,0,-1,1,1,-1,-1,1,-1,-1,1};
struct T
{
    int x,y;
} a,b,p;
int cross(T a,T b,T p)
{
    return (b.x-a.x)*(p.y-a.y)-(b.y-a.y)*(p.x-a.x);
}
void spfa()
{
    memset(vis,0,sizeof vis),memset(d,0x7f,sizeof d);
    a={sx,sy},b={ex,ey};
    d[sx][sy]=s[sx][sy];
    queue<T>Q;
    Q.push(a);
    while(!Q.empty())
    {
        int x=Q.front().x,y=Q.front().y;
        Q.pop();
        vis[x][y]=0;
        for(int i=0; i<8; i++)
        {
            double w=0;
            p.x=x+dir[i][0],p.y=y+dir[i][1];
            if(p.x<0||p.x>=n||p.y<0||p.y>=m) continue;
            if(cross(a,b,p)>0||p.x==ex&&p.y==ey)
            {
                w=s[p.x][p.y];
                if(i>3) w=w+(s[x][y]+s[p.x][p.y])*PI;
                if(d[p.x][p.y]>d[x][y]+w)
                {
                    d[p.x][p.y]=d[x][y]+w;
                    if(!vis[p.x][p.y])Q.push(p),vis[p.x][p.y]=1;
                }
            }
        }
    }
    mi+=d[ex][ey];
}
int main()
{
    cin>>n>>m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin>>s[i][j];
    cin>>sy>>sx>>ey>>ex;
    mi=-s[ex][ey]-s[sx][sy];
    spfa();
    swap(ex,sx),swap(ey,sy);
    spfa();
    printf("%.2f",mi);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值