洛谷P2661 信息传递

https://www.luogu.org/fe/problem/P2661
一个含有n个点的图,图中每个点的出度为1(如果不考虑自环,那么有可能出现出度为0的情况)要求寻找图中最小的环。
做法:
在读取边时去除形成自环的边。然后遍历所有点,删除所有入度为0的点(删除一个点时它通往的下一个节点入度也相应减1,所以如果下一个节点入度变为0,那么也要删除)。然后剩余的所有点一定都属于某一个环的一部分。遍历所有的环,找到最小的环即可。
注意合理删点,并且避免重复遍历(所有在环上的点只需要被遍历一遍,遍历过的点不必再看)我先是因为只从入度判断一个点是否被删除,结果被卡内存(递归层数太多?),后来发现应当加一个标记数组,直接标记一个点是否有效。每次只删除入度为0且被标记为有效的点。删除操作就是把这个点标记为无效,将它抵达的下一个点入度减1。删点操作本身也是一个dfs,是否删除下一个点也要同时满足两个要求:1是下一个点入度为0,2是下一个点的标记仍然处于有效状态。只判断下一个点入度为0就去删除的话会导致重复删点,导致爆内存。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n;
int way[200000+1];
int ans = 0x7f7f7f7f;

int beginn;
int rd[200000+1];
bool isok[200000+1];

void dfs(int p,int t)
{
    isok[p] = 0;
    if(way[p] == beginn)
    {
        if(t < ans)
        {
            ans = t;
        }
        return;
    }
    else
    {
        dfs(way[p],t+1);
    }
}

void deletedfs(int p)
{
    isok[p] = 0;
    rd[way[p]] --;
    if(rd[way[p]] <= 0&&isok[way[p]] == 1)
    {
        deletedfs(way[p]);
    }
}

int main()
{
    scanf("%d",&n);
    memset(rd,0,sizeof(rd));
    memset(isok,1,sizeof(isok));
    for(int i = 1; i<=n; i++)
    {
        scanf("%d",way+i);
        if(way[i]!=i)
            rd[way[i]]++;
    }
    for(int i = 1; i<=n; i++)
    {
        if(rd[i] == 0&&isok[i] == 1)
        {
            deletedfs(i);
        }
    }
    for(int i = 1; i<=n; i++)
    {
        if(isok[i] == 1)
        {
            beginn = i;
            dfs(i,1);
        }
    }
    printf("%d",ans);
    return 0;
}
### P5729 C++ 题目解析 平台上的题目编号为P5729的编程题涉及的是关于点权调整的问题。这类问题通常涉及到树结构中的点权分配与传输,目的是通过一系列的操作使所有节点达到某种特定的状态。 #### 1. 题意概述 给定一棵有N个节点的无根树,每个节点有一个初始点权值。允许执行两种类型的上传下载操作:一种是从某个非叶子节点向其父节点传递一定量的点权;另一种则是相反方向的操作。目标是在不超过K次操作的情况下,让所有的节点点权变为零。 为了实现这一目标,可以采用贪心算法的思想[^3]。具体来说: - 对于任意一颗子树而言,当其中存在多个点权和不为零的情况时,优先处理那些能够一次性解决问题的小范围区域。 - 如果某颗子树内部已经实现了平衡(即整体点权和为零),那么就不必再对其进行额外干预了。 - 只要保证每次都能尽可能地减少未解决部分的数量,最终就能以较少次数完成整个任务。 #### 2. 实现思路 基于上述原理,在实际编码过程中可采取如下措施: - 使用深度优先遍历(DFS)的方法访问整棵树,并记录各个节点及其对应的子树信息; - 计算每棵子树内的总点权以及需要向上层转移的具体数值; - 当遇到具有负数点权的分支时,立即将所需补充的部分累加到当前路径顶端的位置上等待后续分发; - 完成一轮完整的遍历后,再次自顶向下重新分配之前收集起来待下发的权利额度直至全部清零为止。 以下是简单的伪代码描述如何计算并调整各节点间的点权差额: ```cpp void dfs(int u, int fa){ long long sum = w[u]; // 初始化sum为当前节点权重w[u] for (auto v : g[u]) { // 枚举相邻节点v if(v != fa){ // 排除父亲节点fa的影响 dfs(v,u); // 继续深入探索其他孩子节点 // 更新sum加上来自下方子树贡献后的剩余价值 sum += f[v]; // 将超过容量限度之外多余出来的那部分计入全局变量ans中作为一次有效移动 ans += abs(f[v]); } } // 设置f[u]等于经过本轮更新后的最新状态下的净收益情况 f[u]=sum; } ``` 此段程序片段展示了利用递归来追踪每一个可能影响最终结果的因素,并据此作出相应决策的过程[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值