bzoj 4711 小奇挖矿 ——“承诺”类树形dp

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4711

对“承诺”有了更深的了解。

向外和向内要区分,所以 f [ i ][ j ] 表示根向外 j 步有仓库;g[ i ][ j ]表示根向内 j 步有仓库。

转移的时候要注意,要保证承诺的那个地方确实有仓库;通过 cr 之前的孩子 或 当前孩子 的那个地方的承诺来保证;剩下的部分不用保证那儿有仓库,用自己的最小值转移即可;

一棵子树如果承诺自己内部某个地方有仓库,就一定已经有了;但承诺外部的某个地方有仓库却只是承诺;所以可以对 g[ ][ ] 取min来做那个随便的转移,却不能随便对待 f [ ][ ]。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=205,INF=1e9;
int n,d[N],K,hd[N],xnt,to[N<<1],nxt[N<<1];
int f[N][N],g[N][N],mn[N];
void add(int x,int y)
{
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
  to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;
}
void dfs(int cr,int fa)
{
  for(int i=1;i<n;i++)f[cr][i]=d[i],g[cr][i]=INF;
  f[cr][n]=g[cr][n]=INF;
  f[cr][0]=g[cr][0]=K; mn[cr]=K;//mn[cr]=K
  for(int i=hd[cr],v;i;i=nxt[i])
    if((v=to[i])!=fa)
      {
    dfs(v,cr);
    for(int j=0;j<n;j++)
      {
        if(j)
          g[cr][j]=min(g[cr][j]+min(f[v][j+1],mn[v]),
               g[v][j-1]+min(mn[cr],f[cr][j]));
        else g[cr][j]+=min(f[v][j+1],mn[v]);
        
        f[cr][j]=min(f[cr][j]+min(mn[v],f[v][j+1]),f[v][j+1]+mn[cr]);
        //printf("f[%d][%d]=%d g[%d][%d]=%d\n",cr,j,f[cr][j],cr,j,g[cr][j]);
      }
    mn[cr]=g[cr][0];
    for(int j=1;j<n;j++)min(mn[cr],g[cr][j]);
      }
  mn[cr]=g[cr][0];
  for(int i=1;i<n;i++)mn[cr]=min(mn[cr],g[cr][i]);
}
int main()
{
  scanf("%d%d",&n,&K);
  for(int i=1;i<n;i++)scanf("%d",&d[i]);
  for(int i=1,u,v;i<n;i++)
    {
      scanf("%d%d",&u,&v);add(u,v);
    }
  dfs(1,0);
  printf("%d\n",min(mn[1],f[1][0]));
  return 0;
}

 

posted on 2018-10-08 15:01  Narh  阅读(184)  评论(0编辑  收藏  举报

导航