题意:

有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. unity ugui缩放+移动

    本文仅仅记录自己在工作中踩到的ugui的坑.并讲述如何填的坑. 干货罗列在前,不愿意看的,拿东西走人,自己研究: RectTransform m_Rect m_Rect.localPosition m ...

  2. SpringBoot Mybatis 读写分离配置(山东数漫江湖)

    为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用 ...

  3. H5特性 MutationObserver 监听元素 动态改变iframe高度

    这些代码要写在iframe页中执行 <script type="text/javascript"> $(function () { // Firefox和Chrome早 ...

  4. CentOS7修改默认运行级别

    新装了一个虚拟机,图形界面启动太慢,想调整一下按照以前的经验改运行级别,输入: vi /etc/inittab 然后发现跟之前不一样了,在inittab设置不再生效: # inittab is no ...

  5. Linux汇编教程04:寻址方式

    这一节,我们主要来讨论寻址方式,这一点十分重要. 我们上一节有稍微提了一下,内存地址引用的通用格式: 地址或偏移(%基址寄存器, %索引寄存器, 比例因子 ) 结果地址 = 地址或偏移 + %基址寄存 ...

  6. python实战===爬取所有微信好友的信息

    ''' 爬取所有T信好友的信息 ''' import itchat from pandas import DataFrame itchat.login() friends=itchat.get_fri ...

  7. python并发模块之concurrent.futures(二)

    python并发模块之concurrent.futures(二) 上次我们简单的了解下,模块的一些基本方法和用法,这里我们进一步对concurrent.futures做一个了解和拓展.上次的内容点这. ...

  8. MVC 从控制器将数据对象赋值给前端JS对象

    @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport&quo ...

  9. 【学习笔记】动态树Link-Cut-Tree

    这是两个月前写的,看能不能搬运过来…… 动态树是一类维护森林连通性的问题(已纠正,感谢ZQC巨佬),目前最(wo)常(zhi)见(hui)的动态树就是LCT(Link-Cut-Tree),然而LCT似 ...

  10. myeclipse安装插件phpeclipse后进行PHP代码编写

    平常一般写java代码,有时也捣腾一下php,原来安装过zend studio来编写php代码,无奈电脑越来越卡,于是卸载了zend,然后在myeclipse中安装phpeclipse这款插件来完成p ...