ACWing4807.找数字

本文介绍了一道ACWing上的题目,涉及深度优先搜索(DFS)结合最优性剪枝来寻找给定条件下最大的和最小的数字。在搜索最大值时,从9开始按降序尝试每个位置的数字;搜索最小值时,从1开始并保持低位尽可能小。代码中展示了C++实现的详细过程。
摘要由CSDN通过智能技术生成

ACWing4807.找数字

题目

在这里插入图片描述

思路

本题可以使用深搜+最优性剪枝的方法分别搜索最大值和最小值,搜索过程中,利用贪心的思想,搜索最大值时,高位从最大的数字9开始,按顺序向下搜索,只要保证每一位高位尽可能大,最终的结果就是最大的。同理,搜索最小值时,最高位从最小的1开始,其他位从最小的0开始,只要保证每一位高位尽可能的小,最终的结果一定是最小的

代码

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+100;
int m , s;
vector<int> vt , st;
int flag0 = 1;
int flag = 1;


void dfs0 (int sum , int ans)
{
    if(flag0 == 0) return;
    // 结果剪枝
    if(ans == m)
    {
        if (sum == s) flag0 = 0;
        return;
    }
    // 最优性剪枝
    if((m - ans)*9 + sum < s) return;
    
    int begin = ans == 0 ? 1 : 0; 
    
    for(int i = begin ;i <= 9 ;i++)
    {
        st.push_back(i);
        dfs0(sum + i, ans + 1);
        if(flag0 == 0) return;
        st.pop_back();  // 回溯 
    }
}


void dfs1(int sum ,int ans)
{
//	cout << sum <<" " << ans << endl;  //debug
    // 结果剪枝
    if(flag == 0) return;
    if(sum == s && ans)
    {
        flag = 0;
        return;
    }
    else if(sum > s) return;
    if(ans == m) return;
    // 最优性剪枝
    if((m - ans)*9 + sum < s) return;
    
    // 深搜
    for(int i = 9 ;i >= 1 ;i--)
    {
        vt.push_back(i);
        dfs1(sum + i, ans + 1);
        if(flag == 0) return;
        vt.pop_back();  // 回溯 
    }
}

int main()
{
    cin >> m >> s;
    // 特判 
    if(m == 1 && s == 0)
    {
    	cout << 0 << " " << 0;
    	return 0; 
	}
    // 贪心寻找最小值,从高位开始深搜,每位从 0 开始
    dfs0(0 , 0);
    if(!flag0)
    {
        for(auto i : st) cout << i;
    }
    else cout << -1;
    cout << " ";
    
    // 查找最大值
    dfs1(0 , 0);
    if(!flag)
    {
//    	cout << vt.size() << endl; //debug
        for(auto v : vt) cout << v;
        int res = m - vt.size();
        while(res--)
        {
            cout << 0;
        }
    }
    else cout << -1;
    return 0;
}
题目链接:https://www.acwing.com/problem/content/4948/ 题目描述: 给定一棵有 $n$ 个结点的树,结点从 $1$ 到 $n$ 编号,每个结点都有一个权值 $w_i$,现在有 $m$ 次操作,每次操作是将树中编号为 $x$ 的结点的权值加上 $y$,然后询问一些节点是否为叶子节点,如果是输出 $1$,否则输出 $0$。 输入格式: 第一行包含两个整数 $n$ 和 $m$。 第二行包含 $n$ 个整数,其中第 $i$ 个整数表示结点 $i$ 的初始权值 $w_i$。 接下来 $n-1$ 行,每行包含两个整数 $a$ 和 $b$,表示点 $a$ 和点 $b$ 之间有一条无向边。 接下来 $m$ 行,每行描述一次操作,格式为三个整数 $t,x,y$。其中 $t$ 表示操作类型,$t=1$ 时表示将编号为 $x$ 的结点的权值加上 $y$,$t=2$ 时表示询问编号为 $x$ 的结点是否为叶子节点。 输出格式: 对于每个操作 $t=2$,输出一个结果,表示询问的结点是否为叶子节点。 数据范围: $1≤n,m≤10^5$, $1≤w_i,y≤10^9$ 样例: 输入: 5 5 1 2 3 4 5 1 2 1 3 3 4 3 5 2 3 0 1 3 100 2 3 0 1 1 100 2 3 0 输出: 1 0 0 算法1: 暴力dfs,每次都重新遍历整棵树,时间复杂度 $O(nm)$ 时间复杂度: 最坏情况下,每次操作都要遍历整棵树,时间复杂度 $O(nm)$,无法通过此题。 算法2: 用一个 vector<int> sons[n+5] 来存储每个点的所有子节点,这样可以用 $O(n)$ 预处理出每个点的度数 $deg_i$,如果 $deg_i=0$,则 $i$ 是叶子节点,否则不是。 对于每个操作,只需要更新叶子节点关系的变化就可以了。如果某个节点的度数从 $1$ 变成 $0$,则该节点变成了叶子节点;如果某个节点的度数从 $0$ 变成 $1$,则该节点不再是叶子节点。 时间复杂度: 每次操作的时间复杂度是 $O(1)$,总时间复杂度 $O(m)$,可以通过此题。 C++ 代码: (算法2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值