[JLOI2016/SHOI2016]侦察守卫
动态规划:
这个题目真的看题解看了好久!!!最后还是某大佬讲了才懂。。感觉题解的解释有点错误,也可能是我太弱了。
我们设计状态
$g[i][j]$表示当前在$i$节点,处理到了第$k$棵子树时,还能向外扩展$j$层的最小花费。
$f[i][j]$表示当前在$i$节点,处理到了第$k$棵子树时,在$i$节点的子树中向下还有$j$层没有被覆盖时的最小花费。
那么状态转移(现在在点$u$,$v$为新子树):
$g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][i+1])$
意思分别是:
$u$点向上还要覆盖$j$层的话,也就是原来就可以向上覆盖$j$层,加上这棵新的子树的花费
$u$点向上还要覆盖$j$层的话,也就是这棵新子树可以向上覆盖$j+1$层,加上原来子树的花费
那么代码如下:
#include<iostream> #include<cstdio> #define N 500007 #define inf 0x3f3f3f3f using namespace std; int n,m,dis,cnt; int val[N],head[N],f[N][21],g[N][21]; bool vis[N]; struct Edge { int to,nxt; }edge[N<<1]; void Add(int u,int v) { edge[++cnt]=(Edge){v,head[u]}; head[u]=cnt; } void Dfs(int u,int fa) { if(vis[u]) f[u][0]=g[u][0]=val[u]; for(int i=1;i<=dis;++i) g[u][i]=val[u]; g[u][dis+1]=inf; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(v==fa) continue; Dfs(v,u); for(int j=0;j<=dis;++j) g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][j+1]); for(int j=dis;j>=0;--j) g[u][j]=min(g[u][j],g[u][j+1]); f[u][0]=g[u][0]; for(int j=1;j<=dis+1;++j) f[u][j]+=f[v][j-1]; for(int j=1;j<=dis+1;++j) f[u][j]=min(f[u][j],f[u][j-1]); } } int main() { scanf("%d%d",&n,&dis); for(int i=1;i<=n;++i) scanf("%d",&val[i]); scanf("%d",&m); for(int i=1;i<=m;++i) { int in; scanf("%d",&in); vis[in]=true; } for(int i=1;i<n;++i) { int u,v; scanf("%d%d",&u,&v); Add(u,v); Add(v,u); } Dfs(1,0); printf("%d",f[1][0]); return 0; }