首页 > 代码库 > 树形dp小胖守皇宫(vijosP1144)

树形dp小胖守皇宫(vijosP1144)

题目链接:https://vijos.org/p/1144

题解:这道题的动归稍稍有一点的复杂,因为一个节点有可能被它的子节点观察,也有可能被父节点观察;

所以我们这样表示:

  f[i][0](表示当前i节点放了一个看守,即他自己和所有子节点已经被控制好)

  f[i][1](表示当前i节点不放看守,但是他自己和所有子节点已经被控制好)

  f[i][2](表示当前解点不放看守,子节点已被控制好但他自己没被控制)

  f[i][0]=sum(min(f[son][0],f[son][1],f[son][2]));(如果当前节点放的话,那上一步不管怎么样都可以)

  f[i][1]=sum(min(f[son][0],f[son][1]));(需要子节点已经被控制好,但需要注意,如果每个儿子选的都是f[son][j][1],我们需要挑出代价最小的点,改成f[son][j][0],因为我们需要当前结点被控制);

  f[i][2]=sum(f[son][1]);

大概就是这么几种情况,具体程序注释里讲;

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL f[5000][5],cost[5000];
struct ding{
    int to,next;
}edge[10000];
int head[10000],n,m,root,cnt;
bool vis[5000];
void add(int u,int v){edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}
void dfs(int x)
{
  bool fg=false;
  LL minx=2100000000;
  f[x][0]=cost[x];//先加上在这个点放守卫的代价
  for (int i=head[x];i;i=edge[i].next)
  {
      int y=edge[i].to;
      dfs(y);
      f[x][0]+=min(min(f[y][1],f[y][2]),f[y][0]);
      minx=min(minx,f[y][0]-f[y][1]);
        //为了特判
      if (f[y][0]>f[y][1]) f[x][1]+=f[y][1];
      else 
      {
        f[x][1]+=f[y][0];
        fg=true;//如果有任何一次取了f[i][0],就说明不需要特判
    }
    f[x][2]+=f[y][1];
  }
  if (!fg) f[x][1]+=minx;
//特殊情况
}
int main()
{
 int now,x;
 LL k; 
 scanf("%d",&n);
 for (int i=1;i<=n;i++)
 {
   scanf("%d%lld%d",&now,&k,&m);
   cost[now]=k;
   for (int j=1;j<=m;j++)
   {
        scanf("%d",&x);
        add(now,x);
        vis[x]=true;
   }
 }
 for (int i=1;i<=n;i++) if (!vis[i]) root=i;
//先找到根
 dfs(root);
 printf("%lld\n",min(f[root][1],f[root][0]));
 return 0;
} 

 

   

树形dp小胖守皇宫(vijosP1144)