luogu P1272 重建道路

嘟嘟嘟

这好像是一种树上背包。

我们令dp[i][j] 表示在 i 所在的子树中(包括节点 i)分离出一个大小为 j 的子树最少需割多少条边。

那么转移方程就是

  dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k] - 1) (v是u的一个儿子)

理解起来就是在u所在子树中切 j 个节点,等于在u中切 j - k 个节点加上在v所在子树中切 k 个节点所需要切的边数之和。又因为切出来的这两部分要合并得到一个节点数为 j 的,所以要减1.

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("")
13 #define space putchar(‘ ‘)
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 155;
21 inline ll read()
22 {
23     ll ans = 0;
24     char ch = getchar(), last = ‘ ‘;
25     while(!isdigit(ch)) {last = ch; ch = getchar();}
26     while(isdigit(ch)) {ans = ans * 10 + ch - ‘0‘; ch = getchar();}
27     if(last == ‘-‘) ans = -ans;
28     return ans;
29 }
30 inline void write(ll x)
31 {
32     if(x < 0) x = -x, putchar(‘-‘);
33     if(x >= 10) write(x / 10);
34     putchar(x % 10 + ‘0‘);
35 }
36
37 int n, p;
38 vector<int> v[maxn];
39 int du[maxn], siz[maxn];
40 int dp[maxn][maxn];
41
42 void dfs(int now)
43 {
44     siz[now] = 1;
45     for(int i = 0; i < (int)v[now].size(); ++i)
46     {
47         dfs(v[now][i]);
48         siz[now] += siz[v[now][i]];
49         for(int j = siz[now]; j >= 0; --j)
50             for(int k = 1; k < j; ++k)
51                 dp[now][j] = min(dp[now][j], dp[now][j - k] + dp[v[now][i]][k] - 1);
52     }
53 }
54
55 int main()
56 {
57     n = read(); p = read();
58     for(int i = 1; i < n; ++i)
59     {
60         int x = read(), y = read();
61         v[x].push_back(y);
62         du[x]++;
63     }
64     for(int i = 0; i <= n; ++i)
65         for(int j = 0; j <= n; ++j) dp[i][j] = INF;
66     for(int i = 1; i <= n; ++i) dp[i][1] = du[i];
67     dfs(1);
68     int ans = dp[1][p];        //本来就是根节点
69     for(int i = 2; i <= n; ++i) ans = min(ans, dp[i][p] + 1);        //还要切除和父亲连的一条边
70     write(ans); enter;
71     return 0;
72 }

原文地址:https://www.cnblogs.com/mrclr/p/9641999.html

时间: 2025-02-01 08:44:02

luogu P1272 重建道路的相关文章

P1272 重建道路

P1272 重建道路 题目描述 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场.由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的.因此,牧场运输系统可以被构建成一棵树.John想要知道另一次地震会造成多严重的破坏.有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目. 输入输出格式 输入格式: 第1行:2个整数,N和P 第2..N行:每行2个整数I和J,表示

P1272 重建道路(树形dp)

P1272 重建道路 题目描述 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场.由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的.因此,牧场运输系统可以被构建成一棵树.John想要知道另一次地震会造成多严重的破坏.有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目. 输入输出格式 输入格式: 第1行:2个整数,N和P 第2..N行:每行2个整数I和J,表示

luogu 2296 寻找道路

luogu 2296 寻找道路 题目链接:https://www.luogu.org/problemnew/show/P2296 从终点bfs或者dfs,找出所有终点能到达的点. 然后再从1到n看一下出边是否都与终点相连. 然后对于可行的边,做最短路即可. 因为这里的边权是1,所以bfs即可. CODE: #include <iostream> #include <cstdio> #include <queue> const int maxN = 20000 + 7;

[Usaco2002 Feb]Rebuilding Roads重建道路

题目描述 一场可怕的地震后,奶牛用N个牲口棚(1 <= N <= 150,编号1..N)重建了农民John的牧场.奶牛没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是唯一的.因此,牧场运输系统可以被构建成一棵树.John想要知道另一次地震会造成多严重的破坏.有些道路一旦被毁坏,就会使一棵含有P(1 <= P <= N)个牲口棚的子树和剩余子牲口棚分离,John想知道这些道路的最小数目. 输入格式 第1行:2个整数, N和P 第2..N行:每行2个整数I和J,表示节

luogu P2296 寻找道路

题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条件1 的情况下使路径最短. 注意:图G 中可能存在重边和自环,题目保证终点没有出边. 请你输出符合条件的路径的长度. 输入输出格式 输入格式: 输入文件名为road .in. 第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边. 接下来的m 行每行2 个整数x .y ,之间用一个

【luogu P2296 寻找道路】 题解

题目链接:https://www.luogu.org/problemnew/show/P2296 题意:给定起点终点,找一条从起点到终点的最短路径使路上的每个点都能有路径到达终点. 我们先反着建一遍图,然后从终点开始bfs一遍图,标记所有终点可以到达的点.然后再枚举一遍点,如果这个点是终点没法到达的点,那么再枚举这个点所能连接的所有点,如果这些点是终点可以到达的,那么这些点就是不符合条件的.最后在符合条件的点上做一遍最短路. #include <queue> #include <cstd

[luoguP1272] 重建道路

传送门 奇奇怪怪的分组背包. #include <cstdio> #include <cstring> #include <iostream> #define N 151 #define min(x, y) ((x) < (y) ? (x) : (y)) int n, p, cnt, ans = ~(1 << 31); int head[N], to[N], next[N], f[N][N]; inline int read() { int x = 0

重建道路

传送门 我们来看一道比较可做的树形DP. 现在这个数据规模的树形DP都是可以直接n3暴力转移过掉的呀-- 不过这个状态比较特殊,我们用dp[i][j]表示i节点在子树中保留j个节点所需要删去的最小边数. 那么转移方程就是,dp[i][j] = min(dp[i][j],dp[i][j-k] + dp[v][k] - 1);其中那个-1是节点i和节点v之间的连边,那个是要保留的orz,他之前会被重复计算. 所以我们直接这样暴力dp即可,一开始的初始化是dp[i][1] = son[i],son表示

9.12(多米诺骨牌+道路修建+重建道路)

一道dp: 有两个限制条件时可以考虑将其中一个存到维度,另外一个作为值,但一定要理清楚哪个是要优先满足的 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1002 4 int dp[N][10005],a[N],b[N],d[N]; 5 int main() 6 { 7 memset(dp,0x3f3f3f,sizeof(dp)); 8 int n; 9 scanf("%d",&n); 10