一本通 數字轉換 (樹的直徑 兩種求法)

2021-10-09 10:19:39 字數 4070 閱讀 9358

【定義】

我們將一棵樹t = ( v,e )的直徑定義為maxδ ( u,v ) ( u,v ∈ v ),也就是說,樹中所有最短路徑距離的最大值即為樹的直徑。

題意:若是乙個數x的所有約數(不包括他自己)之和sum比他自己小,

那麼x可以轉化成sum,sum也可以成 x。例如 4可以變為 3,1可以變為7

限制所有數字在不超過n的正整數範圍內轉換,求數字變換滿足無重複數字情況下的最多變換步數 。當n=7,最長為 4->3>1>7

解法1,樹形dp

我們令f1[rt]表示以rt為根的樹,根節點到葉節點的最長距離 , f2[rt]表示以rt為根的樹 , 根節點到葉節點的次最長距離 ,並且這個葉節點和最長距離的那個葉節點不在同乙個子樹中。

那麼易知(好吧是因為我不會證明qaq)樹的直徑d=max(f1[i]+f2[i])

很明顯葉子節點就是邊界,遞推過程就是葉->根。

設j為i的孩子節點,

如果f1[j]+w[i][j]>f1[i] 那麼f2[i]=f1[i] ,f1[i]=f1[j]+w[i][j]

否則,如果f1[j]+w[i][j]>f2[i] 那麼f2[i]=f1[j]+w[i][j]

(注意://這裡由於是不同的子樹j不斷更新i,所以可以保證f1[i] f2[i] 不會走同乙個子樹)

簡而言之就是,

孩子節點+距離如果能更新最長鏈,次長鏈就更新為原先最長鏈的值,最長鏈就被孩子節點更新。 如果不能,就看看能不能更新次長鏈。

題目**:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

namespace std;

#define ll long long

#define ull unsigned long long

const

int inf=

0x3f3f3f

;const

double eps=

1e-5

;const

int maxn=

5e4+10;

/*題意:若是乙個數x的所有約數(不包括他自己)之和sum比他自己小,

那麼x可以轉化成sum,sum也可以成 x。例如 4可以變為 3,1可以變為7

限制所有數字變換在不跨越 n的正整數範圍內舉行轉化,求不停舉行數字變換且無重複數字的最多變換步數*/

int sum[maxn]

;//預處理每個數的約數之和

int f1[maxn]

,f2[maxn]

;//f1:以i為根的樹中,i到葉子節點的最長距離,f2 :....次長距離

//直徑就是 max(f1[i]+f2[i]) 就是樹中所有的兩點最短距離中的最大值

//int n;

void

getsum()

//預處理每個數的約數之和}}

intmain()

else

if(f1[i]+1

>f2[sum[i]

]) f2[sum[i]

]=f1[i]+1

;}int ans=-1

;for

(int i=

1;i<=n;i++

) ans=

max(ans,f1[i]

+f2[i]);

printf

("%d\n"

,ans)

;system

("pause");

return0;

}

解法2兩次dfs

從圖中任意一點p出發,到最遠的點w,再從w出發到離w最遠的點q,wq的距離就是樹的直徑,直接兩次dfs就可以求出圖的直徑,樹也是圖的一種,所以一樣的

證明如下:

①若p已經在直徑上,根據樹的直徑的定義可知q也在直徑上且為直徑的乙個端點

②若p不在直徑上,我們用反證法,假設此時wq不是直徑,ab是直徑

—>若ab與pq有交點c,由於p到q最遠,那麼pc+cq>pc+ca,所以cq>ca,易得cq+cb>ca+cb,即cq+cb>ab,與ab是直徑矛盾,不成立,如下圖(其中ab,pq不一定是

直線,畫成直線是為了方便):

—>若ab與pq沒有交點,m為ab上任意一點,n為pq上任意一點。首先還是np+nq>nq+mn+mb,同時減掉nq,得np>mn+mb,易知np+mn>mb,所np+mn+ma>mb+ma,

即np+mn+ma>ab,與ab是直徑矛盾,所以這種情況也不成立,如下圖:

題目**

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

namespace std;

#define ll long long

#define ull unsigned long long

const

int inf=

0x3f3f3f

;const

double eps=

1e-5

;const

int maxn=

5e5+10;

/*題意:若是乙個數x的所有約數(不包括他自己)之和sum比他自己小,

那麼x可以轉化成sum,sum也可以成 x。例如 4可以變為 3,1可以變為7

限制所有數字變換在不跨越 n的正整數範圍內舉行轉化,求不停舉行數字變換且無重複數字的最多變換步數*/

struct

edge[maxn]

;//前向星存圖

int head[maxn]

;int cnt=1;

int sum[maxn]

;//預處理每個數的約數之和

int n;

int mmax,q;

//q為第一次dfs到達的最遠距離

bool vis[maxn]

;void

getsum()

}}void

add(

int v,

int to)

void

dfs(

int st,

int tmax)

//目前到達點和當前最遠距離

vis[st]=1

;for

(int j=head[st]

;j;j=edge[j]

.next)

//前向星遍歷這裡,自己是忘記head[i]是以i出發的最後一條邊的下標 ,還是不夠熟練}}

intmain()

dfs(1,

0);memset

(vis,0,

sizeof

(vis));

//第一次dfs 求出從1出發到達最遠點q

mmax=0;

dfs(q,0)

;printf

("%d\n"

,mmax)

;system

("pause");

return0;

}

一本通 1 1 例 2 種樹

題目傳送門 這題的題目問的是要滿足所有居民的建議,至少要種多少棵樹。對於這種題目,我們首先想到的應該是貪心策略。貪心策略 首先按右端點從小到大排序,因為要求樹最少,所以要盡量放在右端點。然後定義乙個bool陣列判斷該點是否種過樹即可。從右端點開始種,可以讓更多的樹照顧到更右側的端點,這樣就能使種的數...

10001 一本通 1 1 例 2 種樹

題目題目題目 題目描述 某條街被劃為 n條路段,這 n 條路段依次編號為 1 n。每個路段最多可以種一棵樹。現在居民們給出了 hhh 組建議,每組建議包含三個整數 b,e,t,表示居民希望在路段 b 到 e 之間至少要種 t 棵樹。這些建議所給路段的區間可以交叉。請問 如果要滿足所有居民的建議,至少...

一本通題解 題解一維陣列 1107 校門外的樹

時間限制 1000 ms 記憶體限制 65536 kb 題目描述 某校大門外長度為l的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1公尺。我們可以把馬路看成乙個數軸,馬路的一端在數軸0的位置,另一端在l的位置 數軸上的每個整數點,即0,1,2,l,都種有一棵樹。由於馬路上有一些區域要用來建地鐵。這些...