洛谷 P2458 [SDOI2006]保安站岗 (树形dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/imzxww/article/details/81670204

https://www.luogu.org/problemnew/show/P2458 

思路:

dp[x][0]表示选自己,dp[x][1] 表示选儿子,dp[x][2] 表示选父亲,dp方程:dp[x][0]+=min(dp[v][1],min(dp[v][2],dp[v][0]));dp[x][2]+=min(dp[v][0],dp[v][1]);dp[x][1]有一些特殊的地方因为自己不选儿子并不一定所有的儿子都选,所以我们要选一个最优的儿子选。可以记一个f[v][0]-f[v][1]的最小值,最后把这个加进去就行了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1510;
const int inf=0x3f3f3f3f;
vector<int>e[maxn];
int c[maxn];
int dp[maxn][3];
int in[maxn];
void dfs(int x,int pre)
{
    dp[x][0]=c[x];
    for(int i=0;i<e[x].size();i++)
    {
        int v=e[x][i];
        if(v==pre)continue;
        dfs(v,x);
    }
    int f=0,f1=0;
    int mi=inf;
    for(int i=0;i<e[x].size();i++)
    {
        int v=e[x][i];
        f1=1;
        dp[x][0]+=min(dp[v][1],min(dp[v][2],dp[v][0]));
        dp[x][2]+=min(dp[v][0],dp[v][1]);
        if(dp[v][0]<=dp[v][1])
        {
            dp[x][1]+=dp[v][0];
            f=1;
        }
        else
        {
            dp[x][1]+=dp[v][1];
            mi=min(mi,dp[v][0]-dp[v][1]);
        }
    }
    if(!f)dp[x][1]+=mi;
    if(!f1)dp[x][1]=inf;
}
int main()
{
    int n;
    scanf("%d",&n);
    int x,y,k;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        scanf("%d",&c[x]);
        scanf("%d",&k);
        for(int j=0;j<k;j++)
        {
            scanf("%d",&y);
            in[x]++;
            in[y]++;
            e[x].push_back(y);
            e[y].push_back(x);
        }
    }
    dfs(1,-1);
    printf("%d\n",min(dp[1][1],dp[1][0]));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/imzxww/article/details/81670204