昨天,我講解了關於上週日比賽的第一題,這次,我來講一講關於比賽第二題的解題思路與程序。
首先,
題目
B. 齊心抗疫
時間限制:300ms
空間限制:512MB
題目描述
某市有n個縣,並有n-1條雙向高速公路連通這n個縣,每條高速公路的長度爲1。
受疫情影響,第i個縣裏有a[i]個患者。爲了讓疫情較輕的縣幫助疫情嚴重的縣,政府
決定選擇兩個縣x,y,x的疫情較爲不嚴重, y的疫情較爲嚴重(即a[x] <= a[i]),並
讓縣x幫助縣y 。縣x將爲縣y的每一個患者送一份醫療物資,以最短路從x到y運
輸,運送一份醫療物資通過長度爲1的高速公路需要花費1元,由政府掏錢報銷。
請問如果任意選擇兩個縣實施幫扶計劃,政府最多要花多少錢?
輸入描述
第一行,一個正整數 。
第二行, 個正整數,第 個表示 。
接下來 行,每行兩個數 ,表示縣 和縣 之間有一條高速公路。
輸出描述
一個數表示答案。
樣例輸入
8
3 1 4 1 5 9 2 6
1 2
2 3
2 4
1 5
5 6
4 8
3 7
樣例輸出
45
嘿,這輪我們先把題目全部拽出來。
限制及約定
子任務編號 | 分值 | |
---|---|---|
1 | 2 | 18 |
2 | 100 | 30 |
3 | 2000 | 19 |
4 | 50000 | 33 |
對於所有數據,,。
題目分析
這個題目很容易理解,就是通過各方面條件讓政府花更多的錢。。。出題人一定是一個有錢的大佬。
於是,我們按照題目來看,就可以實現一種特別粗糙的算法:直接硬懟!不過經過上一道題,我猜測硬懟是不行的。
硬懟也有硬懟的方法。方法就是暴力枚舉。先枚舉從每一個點出發,通過遞歸查找需要花最多錢的路徑。此時,算法的時間複雜度應該是。我們算一算,以爲基礎,算一算能拿到多少分。首先沒問題,能拿到18分。也差不多,10000,能拿到30分。似乎有點勉強。不用說了, 拿不到分。我們只能看看在實際上測試機器能拿到多少分。
在提交之前,我們需要解決很多問題。
首先,如何存儲可以走的路呢?
我立馬想到了點陣圖,map[i][j]說明i,j之間有通路。這種方法特點是快,但缺點是需要的空間很大,二維數組2000就差不多爆炸了。
其次,遞歸中間如何書寫?
…
還有很多問題,先讓我們寫寫程序吧。
超級慢版程序:
#include<iostream>
using namespace std;
int num[50001];
int map[2001][2001];
int mmax=-9999999;
int n;int sum;
int search(int w,int last,int llast)
{
int mmmax=num[w]>num[llast]? w:llast;
if(mmax<num[mmmax]*sum) mmax=num[mmmax]*sum;
int flag=0;
for(int i=1;i<=n;i++)
{
if(map[w][i]&&i!=last)
{
flag=1;
sum++;
search(i,w,llast);
sum--;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>num[i];
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
map[x][y]=1;
map[y][x]=1;
}
int w;
for(int i=1;i*i<=n;i++)
{
sum=0;
w=search(i,0,i);
}
cout<<mmax;
return 0;
}
來吧,我們來看看得了多少分吧!
很顯然,超時得到48分。我就說,暴力肯定不行。
接着分析題目
接着,我思考思考…ei,我們還沒有剖解這道題終究需要求什麼。
剖析
假設表示從x到y的最短路徑,列出方程式,我們要求的就是:
可以分解爲:
那就很明白了,只要將f(x,y)和x最大化就OK哩;
接下來,我引入一個概念:樹的直徑
在一棵樹裏,最遠的兩個點構成的路徑就是樹的直徑。直徑的兩個端點上距離任何一個點都要遠。
我來證明下吧。
假設點A與點B是在一棵樹上的,且他們之間的線段就是樹的直徑:
在它們之間,有一個瞎參和的 點C。
接着又來一個瞎參和的 的點D。
接着問題來了!從點C到點B是最遠的還是到點D最遠?
到點B?有可能。
到點D?也有可能。來,咱們再來畫圖:
用紅筆圈出的部分顯然相同,剩下的線段比長度可以換成:誰距離A最遠。那就肯定是B了,要不然相對於點A到點B是樹上的最長路徑這條依據。
到此爲止,論證結束,無論如何,點A到點C或點B到點C是最長的路徑。
程序實現
接下來,就是令人歡喜的程序實現部分了!稍微有點長,諒解。大家慢慢看。
#include<iostream>
using namespace std;
int a[50001];
int head[200005],nxt[200005],to[200005];
int de[50001],dee[50001],deep[50001];
int tot=0; //所在點
void add(int u,int v) //增加路徑
{
nxt[++tot]=head[u];
to[tot]=v;
head[u]=tot;
}
void dfs(int x,int y,int *d)
{
d[x]=d[y]+1;
for(int i=head[x];i;i=nxt[i])
{
if(to[i]!=y)
{
dfs(to[i],x,d);
}
}
}
int main()
{
int n,u,v,root2=0,ma=0,root1=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n-1;i++)
{
cin>>u>>v;
add(u,v);add(v,u);
}
dfs(1,0,de);
for(int i=1;i<=n;i++)
{
if(de[i]>ma)
{
ma=de[i];
root1=i;
}
}
dfs(root1,0,dee);
ma=0;
for(int i=1;i<=n;i++)
{
if(dee[i]>ma)
{
ma=dee[i];
root2=i;
}
}
dfs(root2,0,deep);
int res=0;
for(int i=1;i<=n;i++)
{
res=max(res,max(dee[i]-1,deep[i]-1)*a[i]);
}
cout<<res;
return 0;
}
感謝大家支持!
今天看見有人關注我了?哦豁,第一個哦!!!