首页 > 其他分享 >美洲大蠊动态规划

美洲大蠊动态规划

时间:2022-10-26 21:03:20浏览次数:62  
标签:结点 int cnt dfs 儿子 美洲 动态 大蠊 dp

忘得差不多了,来善后一下。

0x1 树形dp

树形dp,说简单了就是在树上按照树从根结点向叶子结点走的递归顺序+dp的状态转移的维护。

所以从根结点开始,维护一个点时,先维护它所有子结点的值,再来维护自己,相当于一个“先递归,再转移”的递归函数。

这是最基本的类型。

0x10 基础的树形dp

就是像开头说的那样的维护,有几种经典类型。

最大权独立集

随便选了一个, UVA1220

关乎到独立集大小,我们需要记录是否选择当前结点。

而唯一性的问题只需要看更新了自己的子树的选择是否唯一。

记得为了让所有结果好统计,我们用一个超根 \(0\) 结点连向真正的根结点,从 \(0\) 开始跑dfs。

多组数据记得清空。

void dfs(int u)
{
    dp[u][0] = 0;//表示没有选u
    dp[u][1] = 1;//选了u,那么至少有u这个结点
    for (int i = 0; i < G[u].size(); ++i)
    {
        int v = G[u][i];
        dfs(v);//先递归
        //再维护
        //先维护不选u的,此时选不选v都可以
        if (dp[v][0] == dp[v][1])//两边子树结果相同,那肯定不唯一了
        {
            dp[u][0] += dp[v][0];
            f[u][0] = 1;//1表示不唯一
        }
        else if (dp[v][0] > dp[v][1])//不选的更优
        {
            dp[u][0] += dp[v][0];
            if (f[v][0] == 1)
            {
                f[u][0] = 1;//选哪种唯一性跟哪种,这么写而不是f[u][0] = f[v][0]是为了保留之前的结果,保险
            }
        }
        else
        {
            dp[u][0] += dp[v][1];
            if (f[v][1] == 1)
            {
                f[u][0] = 1;
            }
        }
        //再维护选了u,此时只能不选v
        dp[u][1] += dp[v][0];
        if (f[v][0] == 1)
        {
            f[u][1] = 1;
        }
    }
    return;
}

最小权覆盖集

P2016

注意,这个题是“某个士兵在一个结点上时,与该结点相连的所有边都可以被瞭望到”,并且要求所有边都要被瞭望到。

所以我们这么考虑:\(dp[i][0]\) 表示没选 \(i\) 时,此子树的最小权覆盖集大小。

那么它的每个儿子结点都要有士兵,这样才能让它连出去的每一条边都被瞭望到。

\(dp[i][1]\) 则选了 \(i\), 所有连出去的边都被瞭望到了,所以它的儿子结点可选可不选。

注意, 要将 \(dp[i][1]\) 初始化为 \(1\), 因为至少选了一个点;这个题是有根树,要建双向边,dfs时记录上一个结点防止重复走一条边导致死循环,从任意一个点作为根开始dfs。

void dfs(int x, int fa){
    dp[x][1] = 1;//初始值
    for (int i = 0; i < T[x].size(); ++i){
    int y = T[x][i];
    if(y == fa){
        continue;
    }//不要走了重复的点
    dfs(y, x);
    dp[x][0] += dp[y][1];
    dp[x][1] += min(dp[y][0], dp[y][1]);
    }
    return ;
}

那再做一个,P2899

额,这次对于一个节点 \(i\),需要考虑它是被父亲节点连接,还是自己搭天线,或者被儿子结点连接。

和P2016不同的是,它要求所有点被看到,P2016要求的是边。

\(dp[i][0/1/2]\) 分别表示由父亲连接,由自己搭线,由儿子连接。

用代码解释把。

void dfs(int u, int fa)
{
    dp[u][1] = 1;//自己搭了线,肯定至少有自己这个发电台。
    int sum = 0, cnt = inf;
    for (int i = 0; i < G[u].size(); ++i)
    {
        int w = G[u][i];
        if (w == fa)
        {
            continue;
        }
        dfs(w, u);
        dp[u][0] += Min(dp[w][1], dp[w][2]);//如果由父亲连接,则自己不是发电台,那么儿子结点就不能通过父亲连接
        dp[u][1] += Min(dp[w][0], Min(dp[w][1], dp[w][2]));//自己是发电台,儿子肯定能连接,所以都可以
        //而对于u让儿子维护时,需要有一个儿子w是发电台,即dp[w][1],其他儿子可以是发电台也可以用儿子维护,即Min(dp[w][1], dp[w][2])
        //于是先假想所有的儿子都选Min(dp[w][1], dp[w][2]),然后如果所有的儿子都选择了dp[w][2],那么cnt绝对>0,此时加上cnt就会使一个儿子取dp[w][1]并且为所有儿子中代价最小的
        sum += Min(dp[w][1], dp[w][2]);
        cnt = Min(cnt, dp[w][1] - dp[w][2]);
    }
    if (cnt > 0)
    {
        sum += cnt;
    }
    dp[u][2] = sum;
    if (G[u].size() == 1 && u != 1)
    {
        dp[u][2] = inf;//特判,如果是叶子节点,不可能用儿子维护
    }
    return;
}

这种覆盖所有点的,类似的还有P2458,P2279。

P2279需要维护儿子和孙子,父亲和爷爷,还有自己,五类,会吐。

所以解决这一类,引入一种贪心:尽量被自己的爷爷维护。

确实,根本不需要解释。

所以对于P2279,这么做:跑dfs给节点标上深度 \(dep\),用 \(diz\),\(dio\),\(dit\) 分别表示被距离为 \(0\) 的点覆盖(自己),被距离为 \(1\) 的(儿子或父亲),被距离为 \(2\) 的(孙子或爷爷)。

然后优先爷爷。

void bfs()
{
    dep[1].d = 0;
    q.push(1);
    while (q.empty() == 0)
    {
        cur = q.front();
        q.pop();
        for (int i = 0; i < G[cur].size(); ++i)
        {
            nxt = G[cur][i];
            dep[nxt].d = dep[cur].d + 1;
            q.push(nxt);
        }
    }
    return;
}

int main()
{
    scanf("%d", &n);
    dep[1].id = 1;
    for (int i = 2; i <= n; ++i)
    {
        dep[i].id = i;
        scanf("%d", &a);
        fa[i] = a;
        G[a].emplace_back(i);
    }
    bfs();
    sort(dep + 1, dep + n + 1, cmp);
    for (int i = 1; i <= n; ++i)
    {
        //cout << fa[dep[i].id] << ' ';
        int u = dep[i].id;
        int v = fa[u];
        int w = fa[v];
        if ((diz[u] == 1 || diz[v] == 1 || diz[w] == 1) || (dio[u] == 1 || dio[v] == 1) || dit[u] == 1)
        {
            continue;
        }//如果已经被覆盖过
        diz[w] = dio[fa[w]] = dit[fa[fa[w]]] = 1;//否则在爷爷上放,把上面的全部赋值
        ++ans;
    }
    printf("%d", ans);
    return 0;
}

当然,这种简洁的贪心还可以运用到别的上面,比如直接生猛地上P3942,k层覆盖,这个要是写树形dp就是想不开,找死。

0x11 树上背包

标签:结点,int,cnt,dfs,儿子,美洲,动态,大蠊,dp
From: https://www.cnblogs.com/Ziqqurat/p/16783294.html

相关文章

  • 子数组相加和为母数组的和的一半(动态规划题目)
    boolIsMagical(vector<int>&vec){intlen=vec.size();intsum=accumulate(vec.begin(),vec.end(),0);if(sum&1)returnfalse;intmid=......
  • 【Vue】动态方法调用
     JS的动态方法调用是通过eval函数实现但是在Vue中是通过组件的$options.methods实现,写这篇的随笔的原因是因为今天为了封装面包屑组件,花了一下午折腾这个动态方法调用......
  • #yyds干货盘点# 动态规划专题:龙与地下城游戏问题
    1.简述:描述给定一个二维数组map,含义是一张地图,例如,如下矩阵游戏的规则如下:1)骑士从左上角出发,每次只能向右或向下走,最后到达右下角见到公主。2)地图中每个位置的值代表骑士要......
  • #yyds干货盘点# 动态规划专题:过河卒
    1.简述:描述棋盘上 A点有一个过河卒,需要走到目标 B点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方......
  • springboot2 jackson 实现动态返回类字段
    问题与需求自从前后端分离的开发模式广泛普及之后,JSON便成为了端到端交互的首选数据结构。我们在使用java开发后端接口的时候,往往会出现我们一个类有十来个字段,但是前......
  • 前端展示中实现批量标签动态生成
    前端展示中实现批量标签动态生成使用过报表的小伙伴,经常会有条码打印、标签打印的需求,一两个标签还好处理,但很多时候我们可能需要的是几十、上百个内容的批量打印,如下图所......
  • 穿戴式动态心电监测设备的发展综述
    (1)临床应用心电图(electrocardiogram,ECG)是心血管疾病的主要诊断方式之一,从宏观上记录心脏细胞的除极和复极过程,一定程度上客观反映了心脏各部位的生理状况。由心肌激......
  • 记一次动态导出excel并且转pdf的辛酸史。
    前言:接到一个需求,需要把用户填写的资料,填写excel模板中。并且导出pdf收到需求后,着手开干。1.先将数据填写到excel1.1.选取了esaypoi框架,因为该框架支持excel模板填......
  • 【转】VUE 动态绑定 html 的 class
            ......
  • mapper.xml中查询(+动态查询)的一些笔记
    1.参数占位符1.#{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值2.${}:拼SQL,会存在SQL注入问题3.使用时机:参数传递都用#{},如果要对表名、列名进行动态......