$Luogu$ $P2016$ 战略游戏
作者:互联网
背景
题意
以节点、儿子个数及其儿子集合的形式给出一棵 \(n\) 个点的无根树(点从 \(0\) 标号至 \(n-1\) ),规定一个节点可以覆盖距离它不超过 \(1\) 的所有节点,求全图能被覆盖住要使用的最少点数。
解法
又是树形\(dp\)模板。
自然地,设 \(f_{x,0/1}\) 表示 \(x\) 节点不使用和使用时覆盖以 \(x\) 为根的子树的最小代价。
问题来了,给定的树没根,咋办?(话说这个傻逼问题足足困扰了我二十分钟。。。。。。)
只要随便选个根就是有根树啦!
为了方便,不妨设 \(1\) 号节点为根,则直接从 \(1\) 号点开始 \(dfs\) 出所有点的父亲即可。
现在回到转移上来。设边集为 \(E\) ,自然地,有 \(f_{x,0}=\sum_\limits{(x,y) \in E} f_{y,1},f_{x_1}=\sum_\limits{(x,y) \in E} \min \{ f_{y,0},f_{y,1} \}+1\) 。那么要求的就是 \(\min \{f_{1,0},f_{1,1} \}\) 。
细节
由于给定的点是 \([0,n-1]\) 内的,考虑读入的时候统一 \(+1\) 移至正整数区间内再做。
代码
\(View\) \(Code\)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ret=0,f=1;
char ch=getchar();
while('9'<ch||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while('0'<=ch&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret*f;
}
int n,x,y,cnt,fa[1505],f[1505][2];
int num,head[300005];
bool vis[1505];
struct edge
{
int ver,nxt;
}e[300005];
inline void adde(int u,int v)
{
e[++num].ver=v;
e[num].nxt=head[u];
head[u]=num;
}
void dfs(int x)
{
vis[x]=1;
for(register int i=head[x];i;i=e[i].nxt)
{
int y=e[i].ver;
if(vis[y])
continue;
vis[y]=1;
fa[y]=x;
dfs(y);
}
}
void dp(int x)
{
f[x][0]=0;
f[x][1]=1;
for(register int i=head[x];i;i=e[i].nxt)
{
int y=e[i].ver;
if(y==fa[x])
continue;
dp(y);
f[x][0]+=f[y][1];
f[x][1]+=min(f[y][0],f[y][1]);
}
}
int main()
{
n=read();
num=0;
for(register int i=1;i<=n;i++)
{
x=read()+1;
cnt=read();
for(register int j=1;j<=cnt;j++)
{
y=read()+1;
adde(x,y);
adde(y,x);
}
}
dfs(1);
dp(1);
printf("%d\n",min(f[1][0],f[1][1]));
return 0;
}
标签:游戏,limits,int,Luogu,P2016,为根,根树,节点 来源: https://www.cnblogs.com/Peter0701/p/11837813.html