蓝桥杯校内赛 大西线调水工程
昨天做了蓝桥杯校内赛,做的是C++组,前几题一如既往的很水。但是最后三题不像以往那么水了。
大致讲讲8-10三题,第八题,大概是讲浮动数组的个数,第九题找直角坐标系中,能组成V字的三个点的组数,第十题也就是这一题,大西线调水工程。上面两题,如果有题目会单独写的,这一次先写第十题。
先上题目
调水工程
受大西线调水工程启发,小明也准备设计一条调水的水渠。
小明经费有限,他只能在一块有限区域内建立一条简单的水渠。
小明首先勘探了地形,在这块地中有一处水源,必须用作水渠的起点。另外,小明还测量了一些点,包括点的位置和高度。如果两个小明测量的点之间的距离不超过 d 且高度不同,小明就可以在这两点之间建立一段水渠,让水从高处流向低处,这一段的长度为两点之间的直线距离(即将横坐标的差的平方加上纵坐标的差的平方加上高度差的平方后再开平方根)。
小明计划只修一条主水渠,不建立分支的水渠。由于水渠能影响的范围与水渠的长度正相关,小明希望水渠尽可能长。
请注意,水渠必须从水源开始修,并且高度应当递减。水渠的不同段可能交叉(建个桥即可)。
输入格式
输入的第一行包含一个整数 n ,表示小明已经测量的点数。
接下来 n 行,每行三个整数 x, y, h,分别表示测量的点坐标为 (x, y),高度为 h。这部分的第一个点即为水源,第一个点的h值大于其他点的h值。
接下来一行包含一个整数 d。
输出格式
输出一行,包含一个实数,四舍五入保留 2 位小数,表示水渠最长能修多长。样例输入
5
1 1 10
2 3 8
4 5 9
1 2 5
4 5 5
8样例输出
10.66
样例说明
在这些点中有两个坐标为 (4, 5) 的点,这是允许的。
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 1000,0 <= h <= 10000,0 <= x, y <= 10000,0 < d < 1e7(10的7次方)。
首先可以明确的是,这个题目表述是有问题的,不然无法得到10.66。这一题我是当作 不考虑高度的距离<d 水渠长度却包括高度 这样子去算的。
当时在考场上,急着吃饭(着实要吐槽比赛从5:30-9:30的事情),并没有想太多,随手dfs交卷吃饭,出来之后,身为ACM扛把子的学弟说他最后一题懵了,要用最短路去改最长路。我???。看了一下题目,确实是不能递归,先放上一份dfs的代码,因为出了考场,没有代码,我网上找了一份,供大家参考,意思是一样的。
//dfs
#include <bits/stdc++.h>
using namespace std;
int n;double d;
struct s{
double x,y,h;
};
s ss[1001];
double ma=0;
int c(s a,s b)
{
return a.h>b.h;
}
void f(int a,double sum,double hh)
{
//cout<<a<<" "<<sum<<" "<<hh<<endl;
if(sum>ma)ma=sum;
for(int i=a+1;i<n;i++)
{
if(ss[i].h<ss[a].h)
{
double y=sqrt((ss[a].x-ss[i].x)*(ss[a].x-ss[i].x)+(ss[a].y-ss[i].y)*(ss[a].y-ss[i].y));
double x=sqrt((ss[a].x-ss[i].x)*(ss[a].x-ss[i].x)+(ss[a].y-ss[i].y)*(ss[a].y-ss[i].y)+(ss[a].h-ss[i].h)*(ss[a].h-ss[i].h));
if(x<=d)
{
f(i,sum+y,ss[i].h);
}
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>ss[i].x>>ss[i].y>>ss[i].h;
}
sort(ss,ss+n,c);
//cout<<ss[0].h<<endl;
cin>>d;
//for(int i=0;i<n;i++)
//{
f(0,0,ss[0].h);
//}
printf("%.2lf\n",ma);
return 0;
}
~~ 深搜的话,就没什么技术含量了。~~
接下来考虑类似迪杰斯特拉的最长路写法,首先考虑水严格从高处流向低处,相同高度也不能流动,再考虑空间意义,因为空间三角形的存在,A->B的长度,和A->C->B的长度,其中C点不在线段AB上时,显然后者大于前者,两边之和大于第三边。如C点在线段AB上,两者长度一样,也就是说可以流过C也可以直接到B。在此题中,经过排序,从大到小排序之后,相邻高度的两个高度层次,水流必定选择两个层次当中的各一个点流过。以此,我们建立邻接矩阵,只有高度严格大于另一高度时,才形成有向边。此粗有一个空间上的优化,就是建立vector数组来代替稀疏矩阵。
将有意义的边全都建立完成后,进行类迪杰斯特拉最长路,和最短路不同的是,每一次都找距离最长的点进入选择集合,并且更新数值。这样一遍下来就能得出答案。
最终得到答案,虽然与案例一样,但是因为没能提交,是事后写的代码,正确率尚不可知。估计肯定是有错误的,希望大佬赐教。
对不起,上面都是错误解法,正确解法 直接暴力,复杂度不超过n*n
此次之后愈发觉得自己是真的菜。
#include <bits/stdc++.h>
#define LL long long
#define NONE -1
using namespace std;
int n;
int d;
double dis[1000+5];
struct point{
int x,y,h;
point(int _x=0,int _y=0,int _h=0):x(_x),y(_y),h(_h){};
bool operator <(const point &a)const{
return h>a.h;
}
}p[1000+5];
double jlh(int a,int b){
return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)+(p[a].h-p[b].h)*(p[a].h-p[b].h));
}
double jlx(int a,int b){
return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].h);
scanf("%d",&d);
sort(p,p+n);
int laststart=0,start=0;
double maxss=0;
for(int i=1;i<n;i++){
if(p[i].h!=p[start].h){
laststart=start;
start=i;
}
double maxs=0;
for(int j=laststart;j<start;j++)
if(jlh(i,j)<d&&jlx(i,j)+dis[j]>maxs)
maxs=jlx(i,j)+dis[j];
dis[i]=maxs;
maxss=max(maxss,maxs);
}
printf("%.2lf\n",maxss);
}