題目:https://www.luogu.org/problemnew/show/P1273ios
題意:一棵樹,葉子節點是用戶,天天邊有一個權值表示花費,每個用戶有一個值表示他們會交的錢。spa
問在不虧本的狀況下,最多能夠選擇多少個用戶,讓他們獲得從根節點(1)發送出的服務。code
思路:原本很天真的覺得是先dfs處理出每一個葉子節點到根的淨利潤,而後揹包。【太傻逼了】blog
可是同一棵子樹上的節點共用了一段路徑,這裏是不用重複算的。get
因此要樹形dp,$dp[i][j]$表示以$i$爲根的子樹上選了$j$個節點。string
$dp[i][j] = max(dp[i][j], dp[i][j-k]+dp[son][k]-e.w)$it
$j$的範圍是$i$的子孫數,這個須要dfs的時候統計,$k$的範圍是$son$這棵子樹的大小。io
1 #include<cstdio> 2 #include<cstdlib> 3 #include<map> 4 #include<set> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<cmath> 9 #include<stack> 10 #include<queue> 11 #include<iostream> 12 13 #define inf 0x3f3f3f3f 14 using namespace std; 15 typedef long long LL; 16 typedef pair<int, int> pr; 17 18 int n, m; 19 const int maxn = 3005; 20 int head[maxn], tot; 21 struct edge{ 22 int to, w, nxt; 23 }e[maxn]; 24 int pay[maxn]; 25 int dp[maxn][maxn]; 26 27 void add(int x, int y, int c) 28 { 29 e[++tot].to = y; 30 e[tot].w = c; 31 e[tot].nxt = head[x]; 32 head[x] = tot; 33 } 34 35 int dfs(int now) 36 { 37 if(now > n - m){ 38 dp[now][1] = pay[now]; 39 return 1; 40 } 41 int sum = 0, son = 0; 42 for(int i = head[now]; i; i = e[i].nxt){ 43 int to = e[i].to; 44 //printf("%d %d\n", now, e[i].to); 45 son = dfs(to); 46 sum += son; 47 for(int j = sum; j > 0; j--){ 48 for(int k = 1; k <= son; k++){ 49 if(j >= k)dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[to][k] - e[i].w); 50 } 51 } 52 } 53 return sum; 54 } 55 56 int main() 57 { 58 memset(dp, ~0x3f, sizeof(dp)); 59 memset(head, 0, sizeof(head)); 60 scanf("%d%d", &n, &m); 61 for(int i = 1; i <= n - m; i++){ 62 int k; 63 scanf("%d", &k); 64 while(k--){ 65 int a, c; 66 scanf("%d%d", &a, &c); 67 add(i, a, c); 68 } 69 dp[i][0] = 0; 70 } 71 for(int i = n - m + 1; i <= n; i++){ 72 scanf("%d", &pay[i]); 73 dp[i][0] = 0; 74 } 75 dfs(1); 76 for(int i = m; i > 0; i--){ 77 if(dp[1][i] >= 0){ 78 printf("%d\n", i); 79 break; 80 } 81 } 82 }