题意:

有n个点组成一棵树,问至少要删除多少条边才能获得一棵有p个结点的子树?

思路:

设dp[i][k]为以i为根,生成节点数为k的子树,所需剪掉的边数。

dp[i][1] = total(i.son) + 1,即剪掉与所有儿子(total(i.son))的边,还要剪掉与其父亲(+1)的边。

dp[i][k] = min(dp[i][k],dp[i][j - k] + dp[i.son][k] - 2),即由i.son生成一个节点数为k的子树,再由i生成其他j-k个节点数的子树。

这里要还原i与i.son和其父亲的边所以-2。

Description

The cows have reconstructed Farmer John’s farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The cows didn’t have time to rebuild any extra roads, so now there is exactly one way to get from any given barn to any other barn. Thus, the farm transportation system can be represented as a tree.

Farmer John wants to know how much damage another earthquake could do. He wants to know the minimum number of roads whose destruction would isolate a subtree of exactly P (1 <= P <= N) barns from the rest of the barns.

Input

  • Line 1: Two integers, N and P

  • Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J’s parent in the tree of roads.

    Output

A single line containing the integer that is the minimum number of roads that need to be destroyed for a subtree of P nodes to be isolated.

Sample Input

11 6

1 2

1 3

1 4

1 5

2 6

2 7

2 8

4 9

4 10

4 11

Sample Output

2


#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#include<cstdio>
#include<sstream>
#include<numeric>//STL数值算法头文件
#include<stdlib.h>
#include <ctype.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<functional>//模板类头文件
using namespace std; typedef long long ll;
const int maxn=6005;
const int INF=0x3f3f3f3f; int n,p,root;
int dp[155][155];
int father[155],son[155],brother[155]; void dfs(int root)
{
int i,j,k,tem;
for(i = 0; i<=p; i++)
dp[root][i] = 10000000;
dp[root][1] = 0;
k = son[root];
while(k)
{
dfs(k);
for(i = p; i>=1; i--)
{
tem = dp[root][i]+1;
for(j = 1; j<i; j++)
tem = min(tem,dp[k][i-j]+dp[root][j]);
dp[root][i] = tem;
}
k = brother[k];
}
} int solve()
{
int ans,i;
dfs(root);
ans = dp[root][p];
for(i = 1; i<=n; i++)//除了根节点,其他节点要想成为独立的根,必先与父节点断绝关系,所以要先加1
ans = min(ans,dp[i][p]+1);
return ans;
} int main()
{
int i,x,y;
while(~scanf("%d%d",&n,&p))
{
memset(father,0,sizeof(father));
memset(son,0,sizeof(son));
for(i = 1; i<n; i++)
{
scanf("%d%d",&x,&y);
father[y] = 1;//记录该点有父亲节点
brother[y] = son[x];//记录兄弟节点
son[x] = y;//记录子节点
}
for(i = 1; i<=n; i++)
{
if(!father[i])//找到根节点
{
root = i;
break;
}
}
printf("%d\n",solve());
} return 0;
} #include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#include<cstdio>
#include<sstream>
#include<numeric>//STL数值算法头文件
#include<stdlib.h>
#include <ctype.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<functional>//模板类头文件
using namespace std; //typedef long long ll;
//const int maxn=6005;
//const int INF=0x3f3f3f3f; const int MAX = 10000; vector <int> tree[160];
//设dp[i][k]为以i为根,生成 节点数 为k的子树,所需剪掉的边数
int a, b, n, p, dp[160][160];
bool son[160]; void dfs(int root)
{
int len=tree[root].size();
for (int i = 0; i < len; i++)
{
dfs(tree[root][i]);//递归调用孩子结点(后根遍历)
for (int j = p; j > 1; j--)//j==1 的情况已经存在 >1 即可
for (int k = 1; k < j; k++)
dp[root][j] = min(dp[root][j], dp[root][j - k] + dp[tree[root][i]][k] - 2);
}
} int main()
{
scanf("%d %d",&n,&p);
memset(son, false, sizeof(son));
for (int i = 0; i < n - 1; i++)
{
scanf("%d %d",&a,&b);
tree[a].push_back(b);
son[b] = true;//记录b是否有儿子
}
int root = 1;
while(son[root])//找父结点
root++;
for (int i = 1; i <= n; i++)
{
//为以i为根,生成节点数为1的子树所需剪掉的边数 每个结点都有个父结点 +1 根结点有个虚拟的父结点,方便统一处理
dp[i][1] = tree[i].size() + 1;
for (int j = 2; j <= p; j++)
dp[i][j] = MAX;
}
dfs(root);
dp[root][p]--;// 与dp方程中+2有关,还原i与其父亲的边,最后i为父节点,则-1
int ans = MAX;
for (int i = 1; i <= n; i++)
ans = min(ans, dp[i][p]);
printf("%d\n",ans);
return 0;
}

再加一种写法

分析:

首先可以明确是一个树形dp题目,状态也很好定义:

dp【root】【j】:以root为根节点的子树,得到 j 个节点的子树需要最少减掉的边数,注意子树中必须保留root节点。否则无法dp

那么很明显的边界条件dp【root】【1】 = num(儿子的个数),因为要只剩一个节点的子树,那么所有的孩子都减掉,这样就为儿子的个数。

那么状态转移方程呢

dp【root】【i】 = min(dp【root】【i-k】+dp【child】【k】 - 1,dp【root】【i】);

其实就是要得到一个i个节点的子树,枚举所有的孩子为k个节点的,当前root保留 i-k 个节点,然后把root和child之间之前被剪断的连接起来,所以这里要减1

注意一些边界条件就OK了

#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#include<cstdio>
#include<sstream>
#include<numeric>//STL数值算法头文件
#include<stdlib.h>
#include <ctype.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<functional>//模板类头文件
using namespace std; typedef long long ll;
const int maxn=500;
const int INF=0x3f3f3f3f; int dp[maxn][maxn];
vector<int> v[maxn];
int num[maxn];
int sum[maxn]; //i为根的树中所有孩子的数目,包括本身 void dfs(int root)
{
sum[root] = 1;//注意这里
if(v[root].size()==0)
{
dp[root][1] = 0;
sum[root] = 1;
return ;
}
for(int i=0; i<v[root].size(); i++)
{
int child = v[root][i];
dfs(child);
sum[root]+=sum[child];
for(int j = sum[root]; j>0; j--)
{
for(int k = 1; k < j ; k++ )
{
dp[root][j] = min(dp[root][j-k]+dp[child][k]-1,dp[root][j]);
}
}
}
} int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
memset(dp,INF,sizeof(dp));
for(int i=1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
num[x]++;
}
for(int i=1; i<=n; i++)
dp[i][1] = num[i];
dfs(1);
int ans = dp[1][m];
for(int i=2; i<=n; i++)
ans = min(ans,dp[i][m]+1);
printf("%d\n",ans);
for(int i=0; i<=n; i++)
v[i].clear();
}
return 0;
} //int num[maxn];
//int sum[maxn]; //i为根的树中所有孩子的数目,包括本身
//int dp[maxn][maxn];//dp[root][j],以root为根得到j个节点的子树最少减掉的边数
//vector<int> v[maxn];
//
//void dfs(int root)
//{
// sum[root] = 1;//注意这里
// if(v[root].size()==0)
// {
// dp[root][1] = 0;
// sum[root] = 1;
// return ;
// }
// for(int i=0; i<v[root].size(); i++)
// {
// int child = v[root][i];
// dfs(child);
// sum[root]+=sum[child];
// for(int j = sum[root]; j>0; j--)
// {
// for(int k = 1; k < j ; k++ )
// {
// dp[root][j] = min(dp[root][j-k]+dp[child][k]-1,dp[root][j]);
// }
// }
// }
//}
//
//int main()
//{
// int n,m;
// while(~scanf("%d%d",&n,&m))
// {
// memset(num,0,sizeof(num));
// memset(sum,0,sizeof(sum));
// memset(dp,INF,sizeof(dp));
// for(int i=1; i<n; i++)
// {
// int x,y;
// scanf("%d%d",&x,&y);
// v[x].push_back(y);
// num[x]++;
// }
// for(int i=1; i<=n; i++)
// dp[i][1] = num[i];
// dfs(1);
// int ans = dp[1][m];
// for(int i=2; i<=n; i++)
// ans = min(ans,dp[i][m]+1);
// printf("%d\n",ans);
// for(int i=0; i<=n; i++)
// v[i].clear();
// }
// return 0;
//}

树形dp(poj 1947 Rebuilding Roads )的更多相关文章

  1. POJ 1947 Rebuilding Roads 树形DP

    Rebuilding Roads   Description The cows have reconstructed Farmer John's farm, with its N barns (1 & ...

  2. POJ 1947 Rebuilding Roads 树形dp 难度:2

    Rebuilding Roads Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 9105   Accepted: 4122 ...

  3. DP Intro - poj 1947 Rebuilding Roads(树形DP)

    版权声明:本文为博主原创文章,未经博主允许不得转载. Rebuilding Roads Time Limit: 1000MS   Memory Limit: 30000K Total Submissi ...

  4. [poj 1947] Rebuilding Roads 树形DP

    Rebuilding Roads Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10653 Accepted: 4884 Des ...

  5. POJ 1947 Rebuilding Roads

    树形DP..... Rebuilding Roads Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8188 Accepted: ...

  6. POJ 1947 Rebuilding Roads(树形DP)

    题目链接 题意 : 给你一棵树,问你至少断掉几条边能够得到有p个点的子树. 思路 : dp[i][j]代表的是以i为根的子树有j个节点.dp[u][i] = dp[u][j]+dp[son][i-j] ...

  7. POJ 1947 Rebuilding Roads (树形DP)

    题意:给一棵树,在树中删除一些边,使得有一个连通块刚好为p个节点,问最少需要删除多少条边? 思路: 因为任一条边都可能需要被删除,独立出来的具有p个节点的连通块可能在任意一处地方.先从根开始DFS,然 ...

  8. POJ 1947 Rebuilding Roads (树dp + 背包思想)

    题目链接:http://poj.org/problem?id=1947 一共有n个节点,要求减去最少的边,行号剩下p个节点.问你去掉的最少边数. dp[u][j]表示u为子树根,且得到j个节点最少减去 ...

  9. POJ 1947 - Rebuilding Roads 树型DP(泛化背包转移)..

    dp[x][y]表示以x为根的子树要变成有y个点..最少需要减去的边树... 最终ans=max(dp[i][P]+t)  < i=(1,n) , t = i是否为整棵树的根 > 更新的时 ...

随机推荐

  1. hdu 2680 Choose the best route (dijkstra算法 最短路问题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2680 Choose the best route Time Limit: 2000/1000 MS ( ...

  2. 用例图(Use Case Diagram)

    用例图(Use Case Diagram) 执行者/参与者(Actor): 表示与您的应用程序或系统进行交互的用户.组织或外部系统.用一个小人表示. 用例(Use Case): 即系统具有的功能,在用 ...

  3. Python 16进制与字符串的转换

    电脑上装了Python2.7和3.3两个版本,平时运行程序包括在Eclipse里面调试都会使用2.7,但是由于某些原因在cmd命令行中输入python得到的解释器则是3.3, 一直没对此做处理,因为这 ...

  4. Perl6 Bailador框架(5):利用正则匹配路径

    use v6; use Bailador; =begin pod 我们在路径设置上, 可以利正则表达式捕获的字符串作为子例程参数 =end pod get '/perl6/(.+)' => su ...

  5. Java 关于微信公众号支付总结附代码

    很多朋友第一次做微信支付的时候都有蒙,但当你完整的做一次就会发现其实并没有那么难 业务流程和应用场景官网有详细的说明:https://pay.weixin.qq.com/wiki/doc/api/js ...

  6. Python3 hashlib模块和hmac 模块(加密)

    hashlib 是一个提供了一些流行的hash算法的 Python 标准库.其中所包括的算法有 md5, sha1, sha224, sha256, sha384, sha512等常用算法 MD5加密 ...

  7. GNU Readline 库及编程简介【转】

    转自:https://www.cnblogs.com/hazir/p/instruction_to_readline.html 用过 Bash 命令行的一定知道,Bash 有几个特性: TAB 键可以 ...

  8. 使用makecontext实现用户线程【转】

    转自:http://blog.csdn.net/cyberlabs/article/details/6920138 使用makecontext实现用户线程 现代Unix系统都在ucontext.h中提 ...

  9. 64_g6

    gsettings-desktop-schemas-devel-3.24.0-1.fc26.x..> 22-Mar-2017 20:46 19386 gsf-sharp-0.8.1-27.fc2 ...

  10. FC4-i386-SRPMS

    [重点] http://archives.fedoraproject.org/pub/archive/fedora/linux/core/6/ http://archives.fedoraprojec ...