Luogu P1272 重建道路
P1272 重建道路
Solution
令dp[i][j]为以i为根的子树中保留j个节点,需要断开的最小边数,且当前子树不与父节点相连。
对于每个点的初始化dp[i][1],要断开与它相连的所有边,即它的度。
那么转移方程即为:
dp[u][j] = min(dp[u][j - k] + dp[v][k] - 2,dp[u][j])
方程中-2的原因是dp[v][k]和dp[u][j - k]分别把u->v的边断掉一次,现在要连接回来。
答案即为max(dp[i][p]),因为它已经成为一个独立的连通块。
代码
#include <bits/stdc++.h>
using namespace std;
const int SZ = 150 + 20;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int fist[SZ],temp,n,p,d[SZ];
int dp[SZ][SZ];
struct zt
{
int v,nxt;
}line[SZ << 1];
inline void add(int x,int y)
{
line[++ temp] = (zt){y,fist[x]};
fist[x] = temp;
}
inline void dfs(int u,int far)
{
dp[u][1] = d[u];
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
if(v == far) continue;
dfs(v,u);
for(int j = p;j >= 2;j --)
for(int k = 1;k < j;k ++) //必须保留u才能连接它的子节点
dp[u][j] = min(dp[u][j - k] + dp[v][k] - 2,dp[u][j]);
}
}
int main()
{
memset(fist,-1,sizeof(fist));
scanf("%d%d",&n,&p);
int a,b;
for(int i = 1;i < n;i ++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
d[a] ++;
d[b] ++;
}
memset(dp,0x3f,sizeof(dp));
dfs(1,1);
int ans = dp[0][0];
for(int i = 1;i <= n;i ++) ans = min(ans,dp[i][p]);
printf("%d\n",ans);
return 0;
}
P3354 [IOI2005]Riv 河流
P3354 [IOI2005]Riv 河流
Solution
令dp[i][j][k][0]为以i为根的子树,j为i的祖先且j为伐木场,建k个伐木场的费用,且i处不为伐木场。
令dp[i][j][k][1]为以i为根的子树,j为i的祖先且j为伐木场,建k个伐木场的费用,且i处为伐木场。(i处的伐木场不包含在k中)
dp[u][s[j]][k][0] = min(dp[u][s[j]][k][0],dp[u][s[j]][k - x][0] + dp[v][s[j]][x][0]);
dp[u][s[j]][k][1] = min(dp[u][s[j]][k][1],dp[u][s[j]][k - x][1] + dp[v][u][x][0]);
对于答案我们只关心u和u的子树建立多少伐木场,不关心u处是否建了,所以把是否在i处建的两种情况合并。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 100 + 20;
const int MAXN = 1000 + 100;
int fist[SZ],temp,dep[SZ],s[SZ],siz;
ll dp[SZ][SZ][52][2];
int n,m,w[SZ];
struct zt
{
int v,nxt,w;
}line[MAXN];
inline void add(int x,int y,int z)
{
line[++ temp] = (zt){y,fist[x],z};
fist[x] = temp;
}
inline void dfs(int u)
{
s[++ siz] = u;
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
dep[v] = dep[u] + line[i].w;
dfs(v);
for(int j = 1;j <= siz;j ++)
for(int k = m;k >= 0;k --)
{
dp[u][s[j]][k][0] += dp[v][s[j]][0][0];
dp[u][s[j]][k][1] += dp[v][u][0][0];
for(int x = 0;x <= k;x ++)
{
dp[u][s[j]][k][0] = min(dp[u][s[j]][k][0],dp[u][s[j]][k - x][0] + dp[v][s[j]][x][0]);
dp[u][s[j]][k][1] = min(dp[u][s[j]][k][1],dp[u][s[j]][k - x][1] + dp[v][u][x][0]);
}
}
}
for(int j = 1;j <= siz;j ++)
for(int k = 0;k <= m;k ++)
{
if(k >= 1)
dp[u][s[j]][k][0] = min(dp[u][s[j]][k][0] + w[u] * (dep[u] - dep[s[j]]),dp[u][s[j]][k - 1][1]);
else
dp[u][s[j]][k][0] += w[u] * (dep[u] - dep[s[j]]);
}
siz --;
}
int main()
{
int vi,di;
scanf("%d%d",&n,&m);
memset(fist,-1,sizeof(fist));
for(int i = 1;i <= n;i ++)
{
scanf("%d%d%d",&w[i],&vi,&di);
add(vi,i,di);
}
dfs(0);
printf("%lld\n",dp[0][0][m][0]);
}
2020.7.14