• [SDOI2006] 保安站岗


    题目链接:https://www.luogu.org/problemnew/show/P2458

    看到题目之后——果断树形DP啊!!

    但是怎么树形DP啊qwq。。。。开始想的是(dp[i][0/1])来表示i节点选不选择。

    那么状态转移方程:

    (dp[i][0]=sum_{v=son(i)}^{}dp[v][1])

    (dp[i][1]=val[i]+sum_{v=son(i)}min(dp[v][0],dp[v][1]))

    然后如果所有小的都是dp[v][0],找一个最小的dp[v][1]加上,然后减去原先对应加上的。

    但是!!这样的写法不对!!

    会被这样一组简单的数据hack掉:

    9
    1 1 1 2
    2 5 3 3 4 5
    3 100 1 6
    4 100 1 7
    5 100 2 8 9
    6 1 0
    7 1 0
    8 1 0
    9 1 0

    为什么呢?是因为我们没有考虑用父亲节点控制该节点的情况

    所以正确的状态设计应该是这样的:

    (dp[i][0])表示节点(i)至少被父亲节点控制。

    (dp[i][1])表示节点(i)至少被子节点控制。

    (dp[i][2])表示节点(i)至少被自己控制。

    那么正确的状态转移方程是:

    (dp[i][0]=sum min(dp[son(i)][1],dp[son(i)][2]))

    (dp[i][1]=sum min(dp[son(i)][1],dp[son(i)][2])+dp[son(i)][2])
    (其中至少有一个是1)

    (dp[i][2]=sum min(dp[son(i)][0],dp[son(i)][1],dp[son(i)][2])+val[i])

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 2010
    using namespace std;
    struct Edge{int nxt,to;}edge[MAXN<<1];
    int edge_number,n,ans=2147483647;
    int val[MAXN],head[MAXN];
    long long dp[MAXN][3];
    void add(int from,int to)
    {
        edge[++edge_number].nxt=head[from];
        edge[edge_number].to=to;
        head[from]=edge_number;
    }
    inline void search(int now,int fa)
    {
        long long sum=0;
        dp[now][2]=val[now];
        for(int i=head[now];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(v==fa) continue;
            search(v,now);
            dp[now][2]+=min(dp[v][2],min(dp[v][1],dp[v][0]));
            sum+=min(dp[v][1],dp[v][2]);
        }
        dp[now][0]=sum;
        dp[now][1]=2147483647;
        for(int i=head[now];i;i=edge[i].nxt)
        {
        	int v=edge[i].to;
        	if(v==fa) continue;
    		dp[now][1]=min(dp[now][1],sum+dp[v][2]-min(dp[v][1],dp[v][2]));
    	}
        return;
    }
    int main()
    {
        
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int u,w,sum;
            scanf("%d%d%d",&u,&w,&sum);
            val[u]=w;
            for(int j=1;j<=sum;j++)
            {
                int v;
                scanf("%d",&v);
                add(u,v);
                add(v,u);
            }
        }
        memset(dp,0x3f,sizeof(dp));
        search(1,0);
        printf("%lld
    ",min(dp[1][1],dp[1][2]));
        return 0;
    }
    
  • 相关阅读:
    基于STM32F103C8T6的超声波测距示例
    ST-LINK 到 SWD接线图
    MDK破解版下载
    CompletableFuture Quasar 等并发编程
    c++中参数传递和函数返回简析
    c++中冒号(:)和双冒号(::)的用法
    c++中,size_typt, size_t, ptrdiff_t 简介
    c++中,保证头文件只被编译一次,避免多重包含的方法
    时间序列分析之一次指数平滑法
    Openv2.1基本数据类型
  • 原文地址:https://www.cnblogs.com/fengxunling/p/9849108.html
Copyright © 2020-2023  润新知