题意:

有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. 省队集训 Day3 吴清华

    [题目大意] 给网格图,共有$n * n$个关键节点,横向.纵向距离均为$d$,那么网格总长度和宽度均为$(n+1) * d + 1$,最外围一圈除了四角是终止节点.要求每个关键节点都要通过线连向终止 ...

  2. bzoj 1914: [Usaco2010 OPen]Triangle Counting 数三角形——极角排序

    Description 在一只大灰狼偷偷潜入Farmer Don的牛群被群牛发现后,贝西现在不得不履行着她站岗的职责.从她的守卫塔向下瞭望简直就是一件烦透了的事情.她决定做一些开发智力的小练习,防止她 ...

  3. 【洛谷 P3809】 【模板】后缀排序

    题目链接 先占个坑,以后再补. \(SA\)的总结肯定是要写的. 等理解地深入一点再补. #include <cstdio> #include <cstring> const ...

  4. 大话Spring Cloud

    研究了一段时间Spring Boot了准备向Spring Cloud进发,公司架构和项目也全面拥抱了Spring Cloud.在使用了一段时间后发现Spring Cloud从技术架构上降低了对大型系统 ...

  5. javascript中的addEventListener与attchEvent

    1.addEventListener 该方法用于向指定元素添加事件句柄 浏览器的支持情况为chrome1.0.ie9+.fireFox1.0.opera7.0 该方法包含三个参数event, func ...

  6. [NOI2008] [bzoj1061] 志愿者招募

    还是一道费用流的题目.话不多说,进入正题. 题意:给定n个点和m种从l到r覆盖一层的费用,求满足所有点的覆盖层数都大等于权值的最小费用 分析:要做到区间修改,看似比较麻烦. 用差分把区间修改变成单点修 ...

  7. linux 3389连接工具Rdesktop

    简单使用 工作机换成战斗机了,改用ubuntu,原来的windows7上东西笔记多,还不想重装.用rdesktop来远程连接windows: sudo apt-get install rdesktop ...

  8. Win7(64bit)搭建SVN

    开始: 第一步:下载SVN客户端程序TortoiseSVN并安装(不习惯英文操作界面的顺便在最底下下载一个语言包),下载地址tortoiseSVN下载(由于系统是64位的,我下载的是TortoiseS ...

  9. Python模块学习 - pyinotify

    pyinotify介绍 pyinotify模块用来监测文件系统的变化,依赖于Linux内核的inotify功能,inotify是一个事件驱动的通知器,其通知接口从内核空间到用户空间通过三个系统调用.p ...

  10. wait与waitpid

    1. 函数原型: #include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc ...