【Luogu】P2016战略游戏(树形DP)
设f[i][j]表示以节点i为根的子树在状态j的情况下的最优解。
j有两种情况。
j=1:i这个根节点有士兵在站岗。
j=0:i这个根节点没有士兵在站岗。
转移方程很好想。
f[x][1]+=min(f[to][0],f[to][1]); f[x][0]+=f[to][1];
这样子。
意思就是:如果根节点已经有人站岗了,那么它的直接子节点可站可不站。就从子节点的两种状态中选一个小的。
如果根节点没有人站岗,那它的所有直接子节点都必须站岗,否则会有道路有人看不到。
代码奉上。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,to; }edge[1000010]; int head[10000],num=0; inline void add(int from,int to){ edge[++num]=(Edge){ head[from],to}; head[from]=num; } int f[1700][3]; int size[2000]; void find(int x,int fa){ size[x]=1; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to!=fa){ find(to,x); size[x]+=size[to]; } } } void dfs(int x,int fa){ f[x][1]=1;f[x][0]=0; if(size[x]==1) return; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to!=fa){ dfs(to,x); f[x][1]+=min(f[to][0],f[to][1]); f[x][0]+=f[to][1]; } } } int ans=0x7fffffff; int main(){ int n=read(); for(int i=1;i<=n;++i){ int p=read(),s=read(); for(int j=1;j<=s;++j){ int q=read(); add(p,q); add(q,p); } } for(int i=0;i<n;++i){ memset(f,0,sizeof(f)); memset(size,0,sizeof(size)); find(i,i); dfs(i,i); ans=min(ans,min(f[i][1],f[i][0])); } printf("%d",ans); return 0; }