有限电视网(luogu P1273

             题目传送门

  做过树上背包的同学应该会觉得这题比较简单,不过如果是没有做过的同学的话可能就...(~~比如我~~)

  首先给大家推荐一道树上背包入门题:poj2486 以及讲解:qwq

  那么这道题怎么做呢?

  我一开始想的是先贪心处理收入大于花费的子树,然后用f[i][j]表示编号为i的节点所代表的子树在花费为j时能够开通的最多的用户数目。然后发现第二维不知道范围,连数组开多大都不知道=-=

  所以我们只好换一种思路,设f[i][j]表示编号为i的节点所代表的子树在开通j个用户时的最大利润(即用户缴费-传输花费)。那么转移方程就是f[i][j] = max{f[v][k]+f[i][j-k]-e[i].val} (特殊地,当j为0时函数值为0;叶子节点x的f[x][1]=w[x]) (v表示i的子节点,val为开通一条边的花费,w[i]为i用户缴费数额)

  一个小优化(60->AC)就是算出每个子节点的用户数,这样就不用每次j都从m开始枚举了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define LL long long
 7 #define RI register int
 8 using namespace std;
 9 const int INF = 0x7ffffff ;
10 const int N = 3000 + 10 ;
11 
12 inline int read() {
13     int k = 0 , f = 1 ; char c = getchar() ;
14     for( ; !isdigit(c) ; c = getchar())
15       if(c == '-') f = -1 ;
16     for( ; isdigit(c) ; c = getchar())
17       k = k*10 + c-'0' ;
18     return k*f ;
19 }
20 struct Edge {
21     int to, next, val ;
22 }e[N<<1] ;
23 int n, m ; int head[N], f[N][N], w[N] ;  // f记录最大利润 
24 inline void add_edge(int x,int y,int z) {
25     static int cnt = 0 ;
26     e[++cnt].to = y, e[cnt].next = head[x], head[x] = cnt, e[cnt].val = z ;
27 }
28 
29 int dfs(int x,int fa) {
30     f[x][0] = 0 ;
31     if(x > n-m) {
32         f[x][1] = w[x] ; return 1 ;
33     }
34     int tot = 0 ; 
35     for(int i=head[x];i;i=e[i].next) {
36         int y = e[i].to ; if(y == fa) continue ;
37         tot += dfs(y,x) ;
38         for(int j=tot;j;j--) {  // 从m开始会T哦qwq
39             for(int k=0;k<=j;k++) {
40                 f[x][j] = max(f[x][j],f[y][k]+f[x][j-k]-e[i].val) ;
41             }
42         }
43     }
44     return tot ;
45 }
46 
47 int main() {
48     n = read(), m = read() ;
49     int mm = n-m ;
50     for(int i=1;i<=mm;i++) {
51         int k = read() ;
52         for(int j=1;j<=k;j++) {
53             int x = read(), z = read() ;
54             add_edge(i,x,z) ; add_edge(x,i,z) ;
55         }
56     }
57     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j] = -INF ;
58     for(int i=n-m+1;i<=n;i++) w[i] = read() ;    
59     dfs(1,0) ;
60     for(int i=m;i>=0;i--) {
61         if(f[1][i] >= 0) {
62             printf("%d",i) ; return 0 ;
63         }
64     }
65 }
原文地址:https://www.cnblogs.com/zub23333/p/8760071.html