問題描述
給你乙個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...