【树上背包】P1273 有线电视网

P1273 有线电视网

注意最里面那一层循环是在枚举物品:“选1个叶子节点”、“选2个叶子节点”……

因为最多只能选(cnt)个叶子节点((cnt)为当前子树中的叶子节点个数),一种优化是先统计出这个(cnt),最里面那层循环从1跑到(cnt)(j)两者之间的最小值就可以结束了,都跑到(j)会TLE。

const int INF = 0x3f3f3f3f;
const int maxn = 3e3+10;

int dp[maxn][maxn];
int num = 0;
int head[maxn];
int n,m;

struct Edge {
    int next, to;
    int dis;
}edges[maxn];

void add_edge(int from, int to,int dis) {
    num++;
    edges[num].next = head[from];
    edges[num].to = to;
    edges[num].dis = dis;
    head[from] = num;
}

int dfs(int u) {
    if (u > n - m) return 1;
    int sum = 0;
    for (int i = head[u]; i; i = edges[i].next) {
        int v = edges[i].to;
        //第一层循环:枚举节点u的每一个儿子,一个儿子代表的子树为一组
        int cnt = dfs(v);
        sum += cnt;
        //这是在数以u为根节点的子树有多少个叶子节点
        for (int j = m; j >= 0; j--) {
            //第二层循环:枚举背包容量
            for (int k = 0; k <= min(cnt,j); k++) {
                //第三层循环:枚举组内物品:选1个用户,选2个用户,选3个用户...
                //显然最多只能选不超过j个用户且不超过cnt个用户,即最多选两者中的最小值
                dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k] - edges[i].dis);
            }
        }
    }
    return sum;
}
//dp[i][j]:=在以i为根节点的子树中,选择j个叶子节点时的最大获利
int main()
{
    //ios::sync_with_stdio(false);
    //while (scanf("%d%d",&n,&m)!=EOF){
    memset(dp, ~INF, sizeof(dp));
    cin >> n >> m;
    for (int i = 1; i <= n-m; i++) {
        int k; cin >> k;
        for (int j = 1; j <= k; j++) {
            int v; int d;
            cin >> v >> d;
            add_edge(i, v, d);
        }
    }
    for (int i = 1; i <= m; i++) cin >> dp[n - m + i][1];
    for (int i = 1; i <= n; i++) dp[i][0] = 0;
    dfs(1);
    for (int i = m; i >= 1; i--) {
        if (dp[1][i] >= 0) {
            cout << i;
            break;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/streamazure/p/13689094.html