传送门:QAQQAQ
题意:在一个树上放置守卫,使每一个节点都至少有相邻一节点放置守卫,使最终经费最少
思路:树形DP
首先会想到没有上司的舞会,0表示不放守卫,1表示放守卫,但考虑到对于当前点不放守卫会有两种情况:
1.其父亲放守卫,则其儿子没必要放守卫
2.其父亲没放守卫,则其儿子必须至少有一个放守卫
所以思路就很明显了:dp1表示当前点放守卫,dp0表示当前点没守卫,其父亲有守卫,dp2表示当前没守卫,父亲没守卫,然后进行递推即可
读入时点可能是不按照顺序的,这点需要注意。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> #define ll long long using namespace std; const ll N=1020+500; const ll inf=2000000000; ll n,w[N],dp[N][3];//0:father1now0 1:now1 2:father0now0 vector<ll> v[N]; ll checkmin(ll x,ll y,ll z) { return min(min(x,y),z); } void dfs(ll u,ll f) { ll bl=0,minn=inf; for(ll i=0;i<(ll)v[u].size();i++) { ll p=v[u][i]; if(p==f) continue; dfs(p,u); dp[u][1]+=min(dp[p][1],dp[p][0]); dp[u][0]+=min(dp[p][2],dp[p][1]); if(dp[p][1]<=dp[p][2]) { bl=1; dp[u][2]+=dp[p][1]; } else { dp[u][2]+=dp[p][2]; minn=min(minn,dp[p][1]-dp[p][2]); } } dp[u][1]+=w[u]; if(!bl) dp[u][2]+=minn; } int main() { scanf("%lld",&n); for(ll i=1;i<=n;i++) { ll tmp,m; scanf("%lld",&tmp); scanf("%lld%lld",&w[tmp],&m); for(ll j=1;j<=m;j++) { ll x; scanf("%lld",&x); v[tmp].push_back(x); v[x].push_back(tmp); } } memset(dp,0,sizeof(dp)); dfs(1,-1); printf("%lld",min(dp[1][1],dp[1][2])); return 0; }