#poj:Fair Distribution

poj:Fair Distribution

#题目描述
在这里插入图片描述

#大概意思要你找到一个最短的方式来使得n * k = m 成立 n只能减小 m只能增加 k为任意一整数
#n减少的数加上m增加的数的和最小
#看着好像只能暴力的算 但是定睛一看 范围 1e8 直接暴力能想到会超时 自己写也是各种优化都来试试撞运气还是卡时间 还要优化算法

#知识点
分块 (我是没看出来-别的大佬说的(⊙ˍ⊙))

#代码

#include<bits/stdc++.h>
using namespace std;
#define maxn 1e9
int n,m;

int main()
{
    int t;
    cin >> t;
    for(int i=1; i<=t; i++)
    {
        cin >> n >> m;
        if(m % n == 0)
        {
            cout << 0 <<endl;
            continue;
        }
        if(m < n)
        {
            cout <<n - m <<endl;
            continue;
        }
        else//问题是咋这样想啊  一般都都只会想到下面的i是x   这个将temp也用上了  能用temp 就用temp 否则就用i
        {
            int ans = maxn;
            for(int i=1; i<=n&&i*i <= m; i++) ///!!! i*i 关键  这个限制条件效果很好
            {
                int temp = (m+i-1)/i;///向上取整  temp  和 i 都有可能是x   x * k = m
                if(n >= temp)//temp 是x
                {
                    int cnt = (m + temp - 1)/temp;//cnt 是 k 向上取整过的  所以*x后 会大于m
                    ans = min(ans, min(n-i,n-temp+cnt*temp - m));//min 中 n-i为整除的情况 后一种就是正常情况
                }
                else // i 是x  temp 太大了 不能为n  x应该小于n
                {
                    ans = min(ans,n-i+i*temp-m);//!!! 这个temp是向上取整过的  否则要 n-i + m - m%i
                }
            }
            cout << ans <<endl;
        }
    }
    return 0;
}

#总结
学习了大佬的算法 一眼看过去看不懂 蒟蒻(汗 ~ : P)自己分析了好久才弄的 但是还是不知道为什么要这样弄 先学会了再说吧 不会就自己带个数据跑一遍就又会了一点点 (⊙.⊙) 就嗯推 没那个悟性

POJ2499—二叉树问题 时间限制:1000ms,空间限制:65536K。 问题描述:二叉树是计算机科学中常见的数据结构。在本问题中,我们将看到一棵非 常大的二叉树,其中结点包含一对整数,树的构造如下: (1)根包含整数对(1,1)。 (2)如果一个结点包含(a,b),则其左孩子结点包含(a+b,b),右孩子结点包含 (a,a+b)。 该问题是给定上述二叉树的某个结点的内容(a,b),假设你沿着最短的路径从树根 行走到给定结点,你能否知道需要经过左孩子的个数(走左路步数)和右孩子的个数(走 右路步数)。 输入格式:第一行包含场景个数。每个场景由一行构成,包含两个整数 i 和 j(1≤i,j ≤2×10^9)的结点(i,j),你可以假设这是上述二叉树中的有效结点。 输出格式:每个场景的输出都以"Scenario #i:"的行开头,其中 i 是从 1 开始的场景编号。 然后输出包含两个数字 l 和 r 的单行(用空格分隔),其中 l 和 r 分别表示从树根遍历到输入的结点需要经过左、右孩子的个数,在每个场景后输出一个空行。 输入样例: 3 42 1 3 4 17 73 输出样例: Scenario #1: 41 0 Scenario #2: 2 1 Scenario #3: 4 6 要求:解题代码要包含丰富的注释并提供详细的算法设计思路,思路分为输入、处理和输出三个部分。用Java语言解题。允许一次输入多个测试用例,并一次性输出多个输入对应的所有结果。
最新发布
11-29
# 题目重述 给定一棵特殊构造的二叉树: - 根节点为 $(1, 1)$ - 若某节点为 $(a, b)$,则其左孩子为 $(a+b, b)$,右孩子为 $(a, a+b)$ 现给出一个节点 $(i, j)$,求从根到该节点路径中**向左走**和**向右走**的次数(即经过左、右子节点的步数)。 --- # 详解 ## 算法设计思路 ### **输入部分** - 第一行是测试场景数 $ T $ - 接下来 $ T $ 行,每行两个整数 $ i $ 和 $ j $,表示目标节点 $(i, j)$ - 所有输入均为有效节点(保证在树中存在) - 支持一次性输入所有测试用例 ### **处理部分** 由于 $ i, j \leq 2 \times 10^9 $,不能模拟正向建树或逐层搜索。必须使用**逆向追踪 + 类似欧几里得算法**的方法: #### 关键观察: - 从 $(a, b)$ 出发: - 左孩子:$(a+b, b)$ → 若当前 $ i > j $,说明是从 $(i-j, j)$ 向左走来的 - 右孩子:$(a, a+b)$ → 若 $ j > i $,说明是从 $(i, j-i)$ 向右走来的 - 因此可从 $(i, j)$ 不断减去较小值,直到变为 $(1,1)$ - 使用**整除优化**避免重复减法(类似 GCD 算法) > 📌 每次 $ i > j $:连续向左走了 $ \left\lfloor (i-1)/j \right\rfloor $ 步 > 📌 每次 $ j > i $:连续向右走了 $ \left\lfloor (j-1)/i \right\rfloor $ 步 #### 示例:$(3,4)$ | 步骤 | 当前 | 判断 | 操作 | 增加 | |------|------|------|------|------| | 1 | (3,4) | $ j>i $ | $ r += (4-1)/3 = 1 $, $ j=4-1×3=1 $ | 右走1步 | | 2 | (3,1) | $ i>j $ | $ l += (3-1)/1 = 2 $, $ i=3-2×1=1 $ | 左走2步 | | 3 | (1,1) | 结束 | 停止 | ✅ 输出:`2 1` ### **输出部分** - 每个场景以 `"Scenario #k:"` 开头 - 下一行输出左走步数 $ l $ 和右走步数 $ r $ - 每个场景后输出一个空行 - 所有结果可在处理完全部输入后统一输出 --- ## Java代码实现(支持批量输入与统一输出) ```java import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int scenarios = sc.nextInt(); // 测试场景数量 // 存储所有结果,最后统一输出 List<String> results = new ArrayList<>(); for (int idx = 1; idx <= scenarios; idx++) { long i = sc.nextLong(); long j = sc.nextLong(); long leftSteps = 0; // 向左走的次数 long rightSteps = 0; // 向右走的次数 // 逆推回根节点 (1,1) while (i != 1 || j != 1) { if (i == 0 || j == 0) break; // 安全防护(理论上不会发生) if (i > j) { // 多次左移:父节点是 (i - k*j, j),最多能减 k 次使得结果 ≥1 long times = (i - 1) / j; leftSteps += times; i -= times * j; } else { // j >= i 且不同时为1 → j > i 时才进行 long times = (j - 1) / i; rightSteps += times; j -= times * i; } } // 构造输出字符串 StringBuilder sb = new StringBuilder(); sb.append("Scenario #").append(idx).append(":\n"); sb.append(leftSteps).append(" ").append(rightSteps).append("\n"); results.add(sb.toString()); } // ✅ 一次性输出所有结果 for (String res : results) { System.out.print(res); } } } ``` --- ## 示例验证(输入样例) 输入: ``` 3 42 1 3 4 17 73 ``` ### 场景1: (42,1) - $ 42 > 1 $,连续左走 $ (42-1)/1 = 41 $ 步 → $ i = 42 - 41×1 = 1 $ - 得到 (1,1),结束 - ✅ 输出:`41 0` ### 场景2: (3,4) - $ j > i $: $ (4-1)/3 = 1 $ → 右走1步 → (3,1) - $ i > j $: $ (3-1)/1 = 2 $ → 左走2步 → (1,1) - ✅ 输出:`2 1` ### 场景3: (17,73) - $ j>i $: $ (73-1)/17 = 4 $ → 右走4步 → (17, 73-4×17)=(17,5) - $ i>j $: $ (17-1)/5 = 3 $ → 左走3步 → (17-3×5,5)=(2,5) - $ j>i $: $ (5-1)/2 = 2 $ → 右走2步 → (2,1) - $ i>j $: $ (2-1)/1 = 1 $ → 左走1步 → (1,1) - 总计:左走 = 3+1 = 4,右走 = 4+2 = 6 - ✅ 输出:`4 6` --- # 知识点 - **逆向路径还原**:利用父子关系反推路径,将树上问题转化为数学迭代过程 - **类欧几里得算法优化**:用整除跳过多步相同方向移动,时间复杂度降至 $ O(\log n) $ - **批量输入输出设计**:使用 `List<String>` 缓存结果,实现“一次输入,统一输出”模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值