【CCCC】L3-018 森森美图 (30分),计算几何+判断三点共线+bfs最短路

problem

L3-018 森森美图 (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

  • 给出一个n*m的矩阵,每个点有点权
  • 求两条分散在起点s到终点t连线两边的最短点权路(可以用斜边),输出权值和
solution
  • 将矩形分成ab两边分别求最短路,转移的时候用三点共线判断是否越界即可。
  • 矩形上求最短路可以直接bfs,当然dijkstra或者spfa也区别不大。
#include<bits/stdc++.h>
using namespace std;
const int inf = 1e9+10;
const int maxn = 110;

struct point{ int x, y;  double dis;};
bool operator!=(point a, point b){return a.x!=b.x||a.y!=b.y;}
bool operator==(point a, point b){return a.x==b.x&&a.y==b.y;}

int n, m;
double sc[maxn][maxn];//分数
point s, t;
void input(){
	cin>>n>>m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			cin>>sc[i][j];
	cin>>s.y>>s.x>>t.y>>t.x;
	s.x++;s.y++;t.x++;t.y++;
	s.dis = sc[s.x][s.y];
}

int flag;//1上半部分,0下半部分
double f[maxn][maxn]; //到i,j为止的最小值
int dir[][2]= {{0,1},{1,0},{-1,0},{0,-1},{-1,-1},{1,-1},{-1,1},{1,1}}; //上下左右+前后左右
int cross(point a,point b,point p){//三角形行列式公式,判断三点是否在一个直线上
	return (b.x-a.x)*(p.y-a.y)-(p.x-a.x)*(b.y-a.y);
}
bool check(point p){//检查p是否合法(越界)
	if(p.x<1||p.x>n||p.y<1||p.y>m)return false;//越界
	if(flag && p!=s&&p!=t && cross(s,t,p)<=0)return false;//1:上半部分但点在下面(起点终点不算)
	if(!flag && p!=s&&p!=t && cross(s,t,p)>=0)return false;//2.下半部分但点在上面
	if(p.dis>=f[p.x][p.y])return false;//不是最小值
	return true;
}
void bfs(){
	//init
	queue<point>q;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			f[i][j] = inf;
	//search
	if(check(s)){
		f[s.x][s.y] = s.dis;
		q.push(s);
	}
	while(q.size()){
		point now = q.front(); q.pop();
		point next;
		for(int i = 0; i < 8; i++){
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
			if(i<4)next.dis = f[now.x][now.y]+sc[next.x][next.y];
			else next.dis=f[now.x][now.y]+sc[next.x][next.y]+(sc[next.x][next.y]+sc[now.x][now.y])*(sqrt(2)-1);
			if(check(next)){
				f[next.x][next.y] = next.dis;
				q.push(next);
			}
		}
	}
}

int main(){
	input();
	double ans = 0;
	flag = 1; bfs(); ans += f[t.x][t.y];//搜上面
	flag = 0; bfs(); ans += f[t.x][t.y];//搜下面
	ans -= sc[s.x][s.y]+sc[t.x][t.y];//重复
	printf("%.2f\n",ans);
	return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小哈里

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

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

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

打赏作者

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

抵扣说明:

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

余额充值