二刷提高课,题解目录在这里— 提高课的题解目录
猛一看好像和上一题一样,不过仔细读题发现这里观察的是宫殿
而上一题我们观察的是边,有什么区别呢?
我们可以发现如果是边的话,每条边肯定至少有一个点会被上
所以我们直接从该节点枚举字节点即可,不用考虑父节点
但是如果是点的话,我们发现这个宫殿不仅可以被自己和字节点观察还能被父节点观察!
这是解决这一题的关键,所以我们需要多设置一个状态
即:f[i][0]子节点观察我,f[i][1]自己观察自己,f[i][2]父节点观察我
关键是如何进行转移,对于f[i][1]来说,取所有节点的min(三状态)之和即可(因为字节点没有了限制所以三种状态取最小即可)
对于f[i][2]来说,取所有节点的min(f[j][0],f[j][1]),对于j来说,父节点i不在观察自己了,只能自己看自己或者找个孩子看自己
f[i][0]字节点来观察自己,但是取哪个字节点呢,所以需要枚举
枚举某个子节点观察自己的同时加上其他节点的min(f[j][0],f[j][1])之和即可
注意这种问题需要加上所有子节点的状态
#include<iostream>
#include<cstring>
using namespace std;
const int N=1510;
int h[N],de[N],ne[N],we[N];
int f[N][3];
int n,dix,root;
bool st[N];
void add(int a,int b)
{
de[dix]=b,ne[dix]=h[a],h[a]=dix++;
}
void dfs(int u)
{
f[u][0]=0,f[u][1]=we[u];
for(int i=h[u];~i;i=ne[i])
{
int j=de[i];
dfs(j);
f[u][1]+=min(f[j][1],min(f[j][0],f[j][2]));
f[u][2]+=min(f[j][1],f[j][0]);
}
f[u][0]=1e9;//不可设为零,因为叶节点不可能被子节点所看到的
//需要先初始化为正无穷
for(int i=h[u];~i;i=ne[i])
{
int j=de[i];
int y=f[j][1];
for(int t=h[u];~t;t=ne[t])
{
int x=de[t];
if(x!=j)y+=min(f[x][1],f[x][0]);
}
f[u][0]=min(f[u][0],y);
}
}
int main()
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int a,b,x;
cin>>a>>we[a]>>b;
while(b--)
{
cin>>x;
add(a,x);
st[x]=true;
}
}
for(int i=1;i<=n;i++)
if(!st[i])
{
root=i;
break;
}
dfs(root);
cout<<min(f[root][0],f[root][1]);
return 0;
}