码迷,mamicode.com
首页 > 其他好文 > 详细

动态dp

时间:2019-08-17 10:51:46      阅读:100      评论:0      收藏:0      [点我收藏+]

标签:独立   开始   pac   战略   直接   printf   最小   www   mamicode   

以前学树型dp留下的题目,没有写,然后过了几个月后又回来写了这道题

战略游戏
这是一道典型的最小点覆盖的模板,蒟蒻采用的是树型dp的做法
\(f[i][0/1]\) 在以 \(i\) 为根的子树中,选或不选当前这个点所需要的最少的点
那么转移方程为:
\(f[i][0]=\sum_{v\in son[i]} f[v][1]\)
\(f[i][1]=\sum_{v\in son[i]} min(f[v][0],f[v][1])+1\)
图片解释:
技术图片
技术图片

然后我们从根节点开始,\(dfs\) 遍历每一个节点,然后在每次遍历完一个节点后进行一次树型 \(dp\) ,注意,这个过程是从叶子结点到根的。
\(Code:\)

#include <iostream>
#include <cstdio>
using namespace std;
struct Node
{
    int t;
    int next;   
}node[30011];
int n,tot;
int f[3011][2],head[3011];
void add(int x,int y)
{
    node[++tot].t=y;
    node[tot].next=head[x];
    head[x]=tot;
    return;
}
void dfs(int u,int fa)
{
    f[u][1]=1; //f[u][1]要初始化为1,因为它要选上自己这个点
    for(int i=head[u];i;i=node[i].next)
    {
        int v=node[i].t;
        if(v!=fa) 
        {
            dfs(v,u);
            f[u][0]+=f[v][1]; //如上面的转移方程
            f[u][1]+=min(f[v][1],f[v][0]);  
        }   
    }   
} 
int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        int step;
        scanf("%d",&step);  
        ++step;
        int k;
        scanf("%d",&k);
        for(int j=1;j<=k;++j)
        {
            int y;
            scanf("%d",&y);
            ++y;
            add(step,y);
            add(y,step);  //因为题目中的标号是0~n-1,我为了方便就将每个节点加了1
        } 
    }
    dfs(1,0);
    printf("%d",min(f[1][0],f[1][1]));
    return 0;
}

但是,如果我们加上一个修改操作,每一次都进行修改,然后询问 \(dp\)值,这应该怎么做呢?
这就需要用到我们的标题:动态\(dp\) 了。
动态dp
这道题目要求的是最大点权独立集权值,我们把上面的方程稍微改一下,注意,是独立集,不是覆盖集
\(f[i][0]=\sum_{v\in son[i]} max(f[v][0],f[v][1])\)
\(f[i][1]=\sum_{v\in son[i]} f[v][0]+a[i]\)
图片解释:
技术图片
技术图片
注:\(a[i]\) 表示节点 \(i\) 的权值
我们首先回想一下矩阵乘法的式子:
\(C_{i,j}=\sum_{k=1}^{p}A_{i,k}* B_{k,j}\)
然后我们来脑补一下\(floyd\) 的转移方程:
\(f_{i,j}=min_{k=1}^{n}f_{i,k}+f_{k,j}\)
嗯,为什么看起来这么相似呢?
好像只是把\(\sum\) 换成了\(min\) ,$* $ 改成了 \(+\) 啊,这样子的矩阵乘法是对的吗?
答案是 \(YES\)
这时候我们就可以想到一种方法,我们能不能把转移方程改写成这种新定义的矩阵乘法的形式,然后用线段树来维护一段区间的矩阵乘法的乘积,从而实现 \(nlogn\) 的时间复杂度呢?
答案是 \(Right!\)
但是,我们上面的转移方程不好直接写成矩阵乘法的形式,我们将它改变一下:
\(g[i][0/1]\) 表示不包含\(i\)处在的重链上的节点(包括 \(i\)
\(g[i][0]=\sum_{v\neq son[i]} max(f[v][0],f[v][1])\)
\(g[i][1]=\sum_{v\neq son[i]} f[v][0]+a[i]\)
那么原来的转移方程就可以改为:
\(f[i][0]=g[x][0]+max(f[son[i][0],f[son[i]][1])\)
\(f[i][1]=g[x][1]+f[son[i][0]\)
改写成矩阵乘法的形式就是:

动态dp

标签:独立   开始   pac   战略   直接   printf   最小   www   mamicode   

原文地址:https://www.cnblogs.com/Call-me-zhz/p/11366427.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!