POJ 1947 Rebuilding Roads (树dp + 背包思想)
题目链接:http://poj.org/problem?id=1947
一共有n个节点,要求减去最少的边,行号剩下p个节点。问你去掉的最少边数。
dp[u][j]表示u为子树根,且得到j个节点最少减去的边数。
考虑两种情况,去掉孩子节点v与去不掉。
(1)去掉孩子节点:dp[u][j] = dp[u][j] + 1
(2)不去掉孩子节点:dp[u][j] = min(dp[u][j - k] + dp[v][k])
综上就是dp[u][j] = min(dp[u][j] + 1, min(dp[u][j - k] + dp[v][k]))
1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <ctime> 10 #include <list> 11 #include <set> 12 #include <map> 13 using namespace std; 14 typedef long long LL; 15 typedef pair <int, int> P; 16 const int N = 155; 17 int dp[N][N], n, p, inf = 1e8; 18 vector <int> edge[N]; 19 //dp[i][j]表示i为子树根,且得到j个节点最少减去的边数。不考虑父节点 20 21 void dfs(int u, int par) { 22 for(int i = 2; i <= p; ++i) { 23 dp[u][i] = inf; 24 } 25 dp[u][1] = 0; //考虑到叶子节点,而非叶子节点在下面的for中会增加 26 for(int i = 0; i < edge[u].size(); ++i) { 27 int v = edge[u][i]; 28 if(v == par) 29 continue; 30 dfs(v, u); 31 for(int j = p; j >= 1; --j) { 32 //有点背包的思想,正序的话dp[u][j]可能由dp[v][k]转移而来,在下面会被重复转移。倒序的话保证转移一次。 33 int temp = dp[u][j] + 1; //去掉v子树 34 for(int k = 1; k < j; ++k) { 35 temp = min(dp[u][j - k] + dp[v][k], temp); 36 } 37 dp[u][j] = temp; //最优 跟最短路思想类似 38 } 39 } 40 } 41 42 int main() 43 { 44 int u, v; 45 while(~scanf("%d %d", &n, &p)) { 46 for(int i = 1; i <= n; ++i) { 47 edge[i].clear(); 48 } 49 for(int i = 1; i < n; ++i) { 50 scanf("%d %d", &u, &v); 51 edge[u].push_back(v); 52 edge[v].push_back(u); 53 } 54 dfs(1, -1); 55 int res = dp[1][p]; //根节点没有父亲 不需要+1 56 for(int i = 2; i <= n; ++i) { 57 res = min(dp[i][p] + 1, res); //其他节点有父节点 去除的话所以+1 58 } 59 printf("%d\n", res); 60 } 61 return 0; 62 }
这题感觉好难想,人笨没办法