藍橋杯校內賽 大西線調水工程

藍橋杯校內賽 大西線調水工程

    昨天做了藍橋杯校內賽,做的是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);

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章