放置街燈(UVA 10859)

2021-07-26 16:55:08 字數 1526 閱讀 4783

問題描述

給你乙個n個點m條邊的無向無環圖,在盡量少的節點上放燈,使得所有燈都被照亮。每盞燈將照亮以它為乙個端點的所有邊。在燈的總數最小的前提下,被兩盞燈同時照亮的邊數應盡量大。

輸入格式

輸入的第一行為測試資料組數t(t≤30)。每組資料第一行為兩個整數n和m(m<n≤1000),即點數(所有點編號為0~n-1)和邊數;以下m行每行為兩個不同的整數a和b,表示有一條邊連線a和b(0≤a,b≤n)。

輸出格式

對於每組資料,輸出3個整數,即燈的總數,被2個燈照亮的邊數和只被乙個燈照亮的邊數。

分析因為是無向無環圖(即森林),所以這道題是樹上的動態規劃。

首先,要先知道一點:對於2個變數a,b,如果要在a最小的前提下,求b的最小值,可以求x=m*a+b的最小值,那麼最後答案即為min(a)=x/m,min(b)=x%m,其中m值要大於a的理論最大值與b的理論最小值之差。道理很簡單,當m足夠大的時候,a決定整個式子的值,而b的值對式子的值不會造成多大的影響,只有當a值不變時,b才能對式子的值造成影響。

那麼,對於這道題來說,有2個待優化的條件:1、燈的總數a最小;2、在1的前提下,被2個燈照亮的邊數b最大。乙個最大,乙個最小,顯然不方便優化,所以在上面結論的提示下,考慮將第二個條件等價為被1個燈照亮的邊數c最小(一條邊只能同時被一盞或者2盞燈照亮),這時候就可以直接套用上面的結論了。設x=m*a+c,這裡m可以取2000,或者更大,但是要注意不能太大,否則在運算時可能會造成資料溢位。

分析到這裡,動態規劃的狀態方程也不難想了,因為對於每個節點i來說,只有2種方案,放燈或者不放燈,但是節點i放燈與否與它的父節點也有關係,所以設d(i,j)為節點i的父節點j是否放燈(j值為1是放燈,0是不放)的最小x值。下面分別對2種方案進行分析:

1、節點i不放燈。那麼i的父節點必須放燈(j=1)或者i本身是根節點。此時d(i,j)=sum,如果i不是根,還要加上1,因為節點i與其父節點這條邊上只有1盞燈

2、節點i放燈。此時d(i,j)=sum+m,同樣的,如果j=0,且i不是根,還得加上1,因為節點i與其父節點這條邊上只有1盞燈。

#include #include #include #include using namespace std;

vectoradj[1010];

int vis[1010][2], d[1010][2], n, m;

int dp(int i, int j, int f)

} if (!j&&f >= 0)

ans++;

if (j || f < 0)

return ans;

}int main()

for (int i = 0; i < m; i++)

memset(vis, 0, sizeof(vis));

int ans = 0;

for (int i = 0; i < n; i++)

printf("%d %d %d\n", ans / 2000, (m - ans) % 2000, ans % 2000);

} return 0;

}

樹形DP(放置街燈,uva 10859)

前面也做了一道很像的題,那道題只要求放置的數目最少,要求覆蓋的是點。在這題中,要求覆蓋的是邊,不但要求放著的數目最少,更要求覆蓋兩次的邊最多。因此貪心法不再適用,最少要多少個點可以貪心求出,但是同時要求覆蓋兩次的邊要最多卻不是貪心法能夠解決的,無論如何都是逃不過動態規劃的。所以一開始用貪心做,做到最...

5664 放置盒子

5664.放置盒子 有乙個立方體房間,其長度 寬度和高度都等於 n 個單位。請你在房間裡放置 n 個盒子,每個盒子都是乙個單位邊長的立方體。放置規則如下 你可以把盒子放在地板上的任何地方。如果盒子 x 需要放置在盒子 y 的頂部,那麼盒子 y 豎直的四個側面都 必須 與另乙個盒子或牆相鄰。給你乙個整...

C new的放置語法

include using namespace std struct node int u node int uu 0 u uu cout create endl node u 1000 cout delete n int main node mem 100 node p mem node q ne...