题意:

有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. Android蓝牙通信总结

    这篇文章要达到的目标: 1.介绍在Android系统上实现蓝牙通信的过程中涉及到的概念. 2.在android系统上实现蓝牙通信的步骤. 3.在代码实现上的考虑. 4.例子代码实现(手持设备和蓝牙串口 ...

  2. 【STSRM10】数学上来先打表

    [算法]DP+数学计数 [题意]给出n个点(不同点之间有区别),求出满足下列条件的连边(双向边)方案(对1004535809取模): 1.每条边连接两个不同的点,每两个点之间至多有一条边. 2.不存在 ...

  3. 【POJ】2892 Tunnel Warfare

    [算法]平衡树(treap) [题解]treap知识见数据结构 在POJ把语言从G++换成C++就过了……??? #include<cstdio> #include<algorith ...

  4. resultAPI示例

    什么是Restfull API Restfull API 从字面就可以知道,他是rest式的接口,所以就要先了解什么是rest rest 不是一个技术,也不是一个协议 rest 指的是一组架构约束条件 ...

  5. python数据处理课程笔记(一)

    一.numpy 1.numpy中所有元素必须是相同的类型 a=[1,2,3,4,'t'] #列表中有str类型,转换为ndarray时所有元素都转换为str类型 arr1=np.array(a) pr ...

  6. python进行机器学习(三)之模型选择与构建

    Scikit-Learn库已经实现了所有基本机器学习的算法,可以直接调用里面库进行模型构建. 一.逻辑回归 大多数情况下被用来解决分类问题(二元分类),但多类的分类(所谓的一对多方法)也适用.这个算法 ...

  7. Java回收方法区中回收的类

    回收的类必须满足下面三个条件才能算是“无用的类” 1.该类所有的实例都已经被回收,也就是说Java堆中不存在该类的任何实例: 2.加载该类的ClassLoader已经被回收: 3.该类对应的java. ...

  8. Python模块学习 - click

    Click模块 click模块是Flask的作者开发的一个第三方模块,用于快速创建命令行.它的作用与Python标准库的argparse相同,但是,使用起来更简单. click是一个第三方库,因此使用 ...

  9. IT界天才少年:比肩雷军、叫板任正非,自己作死了

    两点之间,走弯路和走直路到达的终点是一样的,而他只是走了弯路,他曾在华为身居高位.后又创业.做投资,完全是不差钱的主. 他跌宕起伏的人生中充满传奇,成功.失败.遗憾.矛盾,每个词都不足以形容他,只有这 ...

  10. CiteSeer统计的计算机领域的期刊和会议的影响因子(2005)

    产生自CiterSeer 2005数据库,实际的影响因子可能更高.仅供参考使用.真实的IF还需去官网查看 . OSDI: 3.31 (top 0.08%) . USENIX Symposium on ...