洛谷P1084 (疫情控制)

2021-10-14 15:12:52 字數 4278 閱讀 3686

h 國有 n 個城市,這 n 個城市用 n-1 條雙向道路相互連通構成一棵樹,1 號城市是首都,

也是樹中的根節點。

h 國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市

(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市

的每一條路徑上都至少有乙個檢查點,邊境城市也可以建立檢查點。但特別要注意的是,首

都是不能建立檢查點的。

現在,在 h 國的一些城市中已經駐紮有軍隊,且乙個城市可以駐紮多個軍隊。一支軍隊可

以在有道路連線的城市間移動,並在除首都以外的任意乙個城市建立檢查點,且只能在乙個

城市建立檢查點。一支軍隊經過一條道路從乙個城市移動到另乙個城市所需要的時間等於道

路的長度(單位:小時)。

請問最少需要多少個小時才能控制疫情。注意:不同的軍隊可以同時移動。

第一行乙個整數 n,表示城市個數。

接下來的 n-1 行,每行 3 個整數,u,v,w,每兩個整數之間用乙個空格隔開,表示從城市 u

到城市 v 有一條長為 w 的道路。資料保證輸入的是一棵樹,且根節點編號為 1。

接下來一行乙個整數 m,表示軍隊個數。

接下來一行 m 個整數,每兩個整數之間用乙個空格隔開,分別表示這 m 個軍隊所駐紮的

城市的編號。

乙個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。

這道題是我2020秋季資料結構課程設計的題目,思來想去不會做,在洛谷,csdn借鑑各位大佬的思路後,勉強通過了課設,現貼**及思路如下,希望各位大佬批評指正。

該題要解決的問題是在除首都以外的任意乙個城市駐紮軍隊,讓每支軍隊在二分的時間內盡可能多的控制城市,讓軍隊盡量往首都移動(軍隊越往上,能封住的城市越多),一支軍隊經過一條道路從乙個城市移動到另乙個城市所需要的時間等於道路的長度,即要在最短的時間內控制住疫情;總體思路是樹上倍增+預處理+貪心。

const

int maxsize=

1000

;const

int nsize=20;

int city_number;

//城市數

int army_number;

//軍隊數

int total=0;

//邊的數量*2

int help_army_number=0;

//調整後可用軍隊數

int need_city_number=0;

//調整後仍需軍隊駐紮的節點數

int pre_army_number=0;

//調整前到達根節點的軍隊數

int v_value[

2*maxsize]

;//v_value[tot]:儲存第total次add的v值

int weight[

2*maxsize]

;//weight[total]:儲存第total次add的w值

int next[

2*maxsize]

;//next[total]:儲存第total次add的u值上一次的total'值

int last[maxsize]

;//last[u]:儲存u值上一次add對應的total值

int army_loc[maxsize]

;//army_loc[i]:第i號軍隊的軍隊位置

int city_deep[maxsize]

;//city_deep[i]:第i號城市的深度

int number[maxsize][20

];//number[i][j]:第i個節點的第2^j個祖先節點編號

int dist[maxsize][20

];//dist[i][j]:第i個節點到它第2^j個節點的路徑距離

int left_time=0;

//二分搜尋的初始左邊界

int right_time=0;

//二分搜尋的初始右邊界

bool army_stay[maxsize]

;//army_stay[i]:i號城市是否有軍隊駐紮,有則army_stay[i]=1,否則army_stay[i]=0

bool need_army[maxsize]

;//need_army[i]:i號城市需要軍隊駐紮,need_army[i]=1,否則need_army[i]=0

int army_root_dist[maxsize]

;//army_root_dist[i]:重新調整軍隊後仍停留在根節點的軍隊的可移動距離,但未記錄這個軍隊從**來的

int unstay[maxsize]

;//unstay[i]:城市i在重新調整軍隊後仍然處於無軍隊駐紮,unstay[i]=1,否則unstay[i]=0

void

add_edge

(int u,

int v,

int w)

//新增一條邊(u,v,w),表示從城市u到城市v有一條長為w的雙向道路

void

dfs(

)//用深度優先搜尋方法構造dist陣列和number陣列,需要用到乙個棧

s.push

(v);}}

}}bool

dfs(

int u)

//若當前節點已被駐紮,則返回1

for(

int i=last[u]

;i;i=next[i]

)//遍歷城市u的出邊

is_leaf=1;

//若有一條不是連線著父親節點的邊,說明不是葉子節點if(

!dfs

(v))

//若某個子節點搜尋時遇到路徑未被駐紮的葉子節點(邊境城市),直接返回0}if

(!is_leaf)

//當前節點是葉子節點且未被駐紮

return1;

//沒有遇到路徑未被駐紮的葉子節點,返回1

}bool

check

(int time)

for(

int i=

1;i<=army_number;i++

)//沿當前節點上移軍隊,盡最大努力到達根節點(首都),若無法到達根節點,則停留在最淺節點}if

(number[u][0

]==1&& cnt+dist[u][0

]<=time)

//調整前到達根節點的軍隊

else

}for

(int i=last[1]

;i!=

0;i=next[i]

)//深度優先搜尋尋找路徑上未被駐紮的葉子節點

}sort

(p+1

,p+pre_army_number+1)

;for

(int i=

1;i<=pre_army_number;i++

)//對根節點的需要被駐紮的孩子節點進行初步處理

else

}for

(int i=last[1]

;i;i=next[i]

)//找到仍需要被駐紮的節點並儲存}if

(help_army_number//呼叫後可用軍隊數小於仍需駐紮城市數

sort

(army_root_dist+

1,army_root_dist+help_army_number+1)

;sort

(unstay+

1,unstay+need_city_number+1)

;int i=

1,j=1;

while

(i<=need_city_number && j<=help_army_number)

else}if

(i>need_city_number)

return0;

//利用貪心策略完成最後的分配

}int

main()

right_time +

=w;//二分搜尋初始右邊界

}dfs()

; cin>>army_number;

//輸入軍隊個數

for(

int i=

1;i<=army_number;i++

)//構造軍隊初始資訊

if(army_number//軍隊個數小於非邊境城市個數(不包含首都),則無法控制疫情

int min_time=0;

//最終時間

while

(left_time<=right_time)

//二分答案

else

} cout<}

ps:我用到的是自己寫的乙個棧,用編譯器自帶的棧更好

洛谷P1084 疫情控制

題目 細節比較多的二分 跟lca倍增差不多的思想 首先有這樣乙個貪心思路,深度越低的檢查點越好,而最長時間和深度具有單調性,即給定時間越長,每個軍隊能向更淺的地方放置檢查點。因此可以考慮二分時間,然後判斷軍隊是否可以放置在控制疫情的地方。include define n 1001001 using ...

P1084 疫情控制

p1084 疫情控制 好像二分 倍增 樹上差分是比較熱門的考尻點 會結合在一起考,難度比較大,需要多加練習。現在在解決noip最後的幾道大題,很鵝心。也沒有人做嚮導,很難受qwq 首先這是一棵樹,乙個軍隊肯定是越往上走越好。有大佬說過,對於這種提點的題,要是用倍增 要是時間最短,就是要是用時最長的軍...

P1084 疫情控制

p1084 傳送門 感覺noip t3也是有點東西的 將該題轉化為最大值最小問題後想到二分答案 接下來考慮 check 時如何貪心 由於除了在根節點所有軍隊都只往上跳明顯採取倍增的方式 記錄所有能到達根節點的軍隊和根節點下所有未被封死的子樹 將兩個序列從小到大排序後貪心匹配即可判斷 注意 在判斷封死...