zoukankan      html  css  js  c++  java
  • POJ 1155 TELE(树形DP)

    题意:

    给定一棵树, 树的叶子节点是用户, 每个用户会提供一定的收益, 其他节点都是中间节点. 中间节点之间和中间节点到叶子节点上都有耗费. 求解在总耗费小于总收益的情况下所能得到的最多用户数

    思路:

    1. 树形DP, DP[i][j] 表示在以第 i 个节点为根的树上上保留 j 个用户的最大收益. 这样, 求解 dp[i][j] >=0 时最大的 j 即可. 这个 dp 的定义没想到

    2. DP[i][j] = min(DP[i][j], DP[i][j-k]+DP[v][k]-c), c 表示节点 i 到节点 v 的耗费

    总结:

    1. 初始化时, dp[u][0] = 0, dp[u][else] = -INF. 初始化紧扣 DP 的定义, 符合逻辑

    细节:

    1. 初始化为 -INF, 求 max 时, 两个 -INF 相加可能会溢出, 因此进行了一步判断 if(dp[u][j-k] != -INFS && dp[v][k] != -INFS), 当然 ,不进行判断也是可以的, 这时要求 INF 不要设置的太大(1e9就不需要判断, 2*1e9 不会溢出), 这个技巧上一篇也讨论过

    2. 下面代码中 j 的遍历若是从 N 开始, 则会 TLE. 从 N-M 开始会不会 LTE 没试过. 但是从 children[u] 开始倒是 AC 了. 计算 children 的时候, 我本打算在以 children += dfs(v) 的形式计算, 不过这似乎是不可能的, 只能单列一个方法, 单独 pass 一遍树. 另外, children[叶子] == 1 不合逻辑, 不过这倒是方便了计算

    3. 还有一个技巧, 是对 dp[叶子][1] 的初始化, 减少了分支的判断, 少用了一个数组, 缺点是 dfs 函数的初始化乱了一点

    for(int i = t+1; i <= N; i ++) {
      cin >> dp[i][1]; 
    }

    代码:

    #include <iostream>
    #include <vector>
    using namespace std;
    class node {
    public:
    	int id, ct;
    	node(int _id, int _ct):id(_id), ct(_ct){}
    	node() {
    		node(0,0);
    	}
    };
    const int INFS = 1E9;
    const int MAXN = 3200;
    int N, M, K;
    vector<node> graph[MAXN];
    int cost[MAXN];
    int dp[MAXN][MAXN];
    int children[MAXN];
    void dfs(int u) {
    	for(int i = 2; i <= N; i ++) 
    		dp[u][i] = -INFS;
    	dp[u][0] = 0;
    	if(u > N-M) {
    		return;	
    	}
    	dp[u][1] = -INFS;
    	
    	for(int i = 0; i < graph[u].size(); i ++) {
    		int v = graph[u][i].id;
    		int c = graph[u][i].ct;
    		dfs(v);
    		for(int j = children[u]; j > 0; j --)  
    			for(int k = 0; k <= j; k ++) 
    				//if(dp[u][j-k] != -INFS && dp[v][k] != -INFS) // 防止溢出
    					dp[u][j] = max(dp[u][j], dp[u][j-k]+dp[v][k]-c);
    	}
    	
    }
    void getChildren(int u) {
    	if(u > N-M) {
    		children[u] = 1;
    		return;
    	}
    	for(int i = 0; i < graph[u].size(); i ++) {
    		int v = graph[u][i].id;
    		getChildren(v);
    		children[u] += children[v];
    	}
    
    }
    int main() {
    	freopen("E:\Copy\ACM\poj\1155\in.txt", "r", stdin);
    	while(cin >> N >> M) {
    		int t = N-M;
    		for(int i = 0; i < t; i ++) {
    			cin >> K;
    			
    			int id, cost;
    			for(int j = 0; j < K; j ++) { 
    				cin >> id >> cost;
    				graph[i+1].push_back(node(id, cost));
    			}
    		}
    		for(int i = t+1; i <= N; i ++) {
    			cin >> dp[i][1]; // 减少了很多分支判断条件
    		}
    		getChildren(1);
    		dfs(1);
    		for(int i = N; i >= 0; i--) {
    			if(dp[1][i] >= 0) {
    				cout << i << endl;
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    电子工程师的血泪史
    最简单的bootloader的编写步骤
    6811汇编语言
    Tiny6410SHT10温湿度传感器
    Tiny6410裸机程序
    无线收发模块NRF24LU1和NRF24LE1
    转载工作10年的人总结的6句话
    Tiny6410取模软件设置
    别人的感悟
    在Windows下用Virtualbox虚拟linux时共享文件夹设置的方法
  • 原文地址:https://www.cnblogs.com/xinsheng/p/3433166.html
Copyright © 2011-2022 走看看