最小点队的题意:https://www.luogu.com.cn/problem/P2899
与战略游戏不同的是,这里要求占领所有的点而不是边。
1自己被自己染色(有信号塔)
这时我们可以想一下,u被自己染色可以由什么转移过来,如果u已经被自己染色了的话,他的儿子v可以选择自己染色,也可以选择被自己(vv)的儿子染色,当然也可以被u染色,当然,我们要选最小的,所以转移方程就是
f [ u ] [ 0 ] += min ( f [ v ] [ 0 ] , f [ v ] [ 1 ] , f [ v ] [ 2 ] )
2.被自己的父亲结点染色(自己没有信号塔)
如果被父亲结点(fa)染色了,那么u的儿子v只能选择自己染色或者被它的儿子染色,转移方程为
f [ u ] [ 1 ] += min ( f [ v ] [ 0 ] , f [ v ] [ 2 ] )
3.被自己的儿子结点染色(自己没有信号塔)
这是最麻烦的一种情况,因为u可能有多个儿子,只要有一个儿子自己染色了,就可以将u覆盖,这种情况就成立了。
那我们就用一个flag记号来考察,在转移的时候,是不是曾经从有信号塔的儿子( dp [ v ] [ 0 ] )转移过来
如果有,那皆大欢喜,什么都不用做。
相反,如果没有,就说明所有状态都是由没有信号塔的儿子( dp [ v ] [ 2 ] ) 转移而来
那我们就应该找一个儿子,把那个儿子换成 dp [ v ] [ 0 ]
找哪个儿子呢?当然是 dp [ v ] [ 0 ] - dp [ v ] [ 2 ] 最小的那个儿子
#include <bits/stdc++.h> using namespace std; const int maxn=10009; int n; vector<int>vec[maxn]; int vis[maxn],dp[maxn][4]; //dp[now][0]自己覆盖自己,放信号塔 //dp[now][1]父亲覆盖自己,本身不放信号塔 //dp[now][2]儿子覆盖自己,本身不放信号塔 void dfs(int now) { int flag=0,p=99999999; dp[now][0]=1; for(int i=0;i<vec[now].size();i++) { int w=vec[now][i]; if(vis[w]) continue; vis[w]=1; dfs(w); dp[now][0]+=min(dp[w][0],min(dp[w][1],dp[w][2]));//儿子随意 dp[now][1]+=min(dp[w][0],dp[w][2]); // dp[now][2]+=min(dp[w][0],dp[w][2]); if(dp[w][0]<=dp[w][2]) { flag=1; dp[now][2]+=dp[w][0]; } else { dp[now][2]+=dp[w][2]; p=min(p,dp[w][0]-dp[w][2]); } } if(flag==0) dp[now][2]+=p; } int main() { cin>>n; for(int i=1;i<=n-1;i++) { int l,r; cin>>l>>r; vec[l].push_back(r); vec[r].push_back(l); } vis[1]=1; dfs(1); cout<<min(dp[1][2],dp[1][0]); }