一、题目
二、解法
交换操作真的很难做,而且距离是难以记录的,所以套路的 (dp) 方法是行不通的。
对于交换操作有一个神奇的转化:由于交换是全局任意交换的,我们考虑记录已经用到的黑点个数,如果用到的黑点个数不超过原有的黑点个数那么就是一种合法的操作,然后我们不记录距离去记录解决该点问题的点。
设 (dp[u][i][t]) 表示以 (u) 为根的子树中,有 (i) 个关键点,其中解决 (u) 问题的节点在 (t)(我们称 (u) 使用 (t))的最小交换次数。转移考虑合并子树,为了保证转移的正确性,我们要在把 (t) 合并到 (u) 上的时候考虑 (t) 为黑点的代价:
- 考虑合并子树,(dp[u][i+j][t]leftarrow dp[u][i][t]+dp[v][j][t]),也就是它们共同使用一个黑点 (t),那么如果 (t) 在 (u) 子树中 (t) 为黑点的代价就会被考虑到。
- 如果 (u,v) 所使用的黑点不同,那么 (v) 使用的节点一定要在其子树中,所以设 (mn=min dp[v][j][t]),那么如果 (t otin v) 的子树,那么 (dp[u][i+j][t]leftarrow dp[u][i][t]+mn)
初始化:(dp[u][1][u]=1-a[u]),如果 (d(u,v)leq x),那么 (dp[u][0][v]=0)
因为第二维的是树背包,所以总时间复杂度 (O(n^3)),要用 ( t short) 来卡空间。
三、总结
交换操作是难做的,但是我们用记录状态的方法解决了此问题(用黑点个数描述合法交换),所以记录状态竟然能解决操作的问题!
本题还有一个转化是记录每个点使用的点,但是需要注意何时考虑代价,所以这题是代价延后计算,这十分适用于全局操作问题中。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 501;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,tot,a[M],f[M],x,cnt,pd[M][M],dfn[M],sz[M];
short dp[M][M][M],g[M][M],ans=0x3f3f;
struct edge
{
int v,c,next;
}e[2*M];
short min(short a,short b) {return a<b?a:b;}
void work(int u,int rt,int w)
{
pd[rt][u]=1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(!pd[rt][v] && w+c<=x)
work(v,rt,w+c);
}
}
void pre(int u,int fa)
{
dfn[u]=++cnt;
for(int i=f[u];i;i=e[i].next)
if(e[i].v^fa) pre(e[i].v,u);
}
void dfs(int u,int fa)
{
sz[u]=1;
dp[u][1][dfn[u]]=!a[u];
for(int v=1;v<=n;v++)
if(u!=v && pd[u][v]) dp[u][0][dfn[v]]=0;
for(int w=f[u];w;w=e[w].next)
{
int v=e[w].v;
if(v==fa) continue;
dfs(v,u);
for(int i=0;i<=sz[u];i++)
for(int t=1;t<=n;t++)
g[i][t]=dp[u][i][t],dp[u][i][t]=0x3f3f;
for(int i=0;i<=sz[u];i++)
for(int j=0;j<=sz[v] && i+j<=m;j++)
{
int mn=*min_element(dp[v][j]+dfn[v],dp[v][j]+dfn[v]+sz[v]);
for(int t=1;t<=n;t++)
{
dp[u][i+j][t]=min(dp[u][i+j][t],g[i][t]+dp[v][j][t]);
if(t<dfn[v] || t>=dfn[v]+sz[v])
dp[u][i+j][t]=min(dp[u][i+j][t],g[i][t]+mn);
}
}
sz[u]+=sz[v];
}
}
signed main()
{
n=read();x=read();
for(int i=1;i<=n;i++)
a[i]=read(),m+=a[i];
for(int i=1;i<n;i++)
{
int u=read(),v=read(),c=read();
e[++tot]=edge{v,c,f[u]},f[u]=tot;
e[++tot]=edge{u,c,f[v]},f[v]=tot;
}
pre(1,0);
for(int i=1;i<=n;i++)
work(i,i,0);
memset(dp,0x3f,sizeof dp);
dfs(1,0);
for(int i=0;i<=m;i++)
for(int j=1;j<=n;j++)
ans=min(ans,dp[1][i][j]);
if(ans==0x3f3f) puts("-1");
else printf("%d
",ans);
}