哎…还是喜欢建双向边之后dfs遇到 j == fa
就 continue;
的写法啊!
多的不说了,都在注释里了
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5000;
const int INF = 0x3f3f3f3f;
int ver[N],head[N],val[N],nex[N],idx;
int f[N][3];//三种状态 0父亲节点 1子节点 2自己
int n,ans;
void add(int from,int to)
{
ver[++idx] = to;
nex[idx] = head[from];
head[from] = idx;
}
void dfs(int u,int fa)
{
f[u][0] = 0;
f[u][1] = INF;
f[u][2] = val[u];//初始化为自己的代价
for(int i=head[u];i;i=nex[i])
{
int j = ver[i];//拿到这个节点
if(j == fa) continue;
dfs(j,u);
f[u][0] += min(f[j][1] , f[j][2]) ;
//u被父节点支配 则其子节点被再子节点支配 或者 被自己支配
f[u][2] += min(min(f[j][0],f[j][1]),f[j][2]);
//u被自己支配 则其子节点都可以
}
//现在再去考虑 u被某一个子节点支配的情况 必须有一个子节点是被自己支配的
//或而言之,必须有一个点是被选的其余可以都不选/随便选几个
//那我们枚举以每个节点为必选点的情况,其他子节点随意
for(int i=head[u];i;i=nex[i])
{
int j = ver[i];
if(j == fa) continue;
//枚举点 j 为必选点
int res = f[j][2] + f[u][0] - min(f[j][1] , f[j][2]);
//选节点 j 的点数 + 其他节点随便选的点数
//但是我们f[u][0]实质上还包括了节点 j 随便选择的情况
//我们减去它在 f[u][0] 中的贡献即可
f[u][1] = min(f[u][1] , res );
}
}
signed main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
int id,cost,m;
cin >> id >> cost >> m;
val[id] = cost ;//该节点部署所需权值
for(int j=1;j<=m;j++)
{
int b;
cin >> b;
add(id,b);
add(b,id);
}
}
dfs(1,0);
ans = min(f[1][1] , f[1][2]);//由子节点或者自己支配
cout << ans << endl;
return 0;
}