尺取+二分查找

尺取法:

这是一种比较有趣的方法,想吃子一样去解决问题。
现在我只是知道了可以用数组来模拟“尺子”。加油学习!

方法是:数组模拟+二分查找

说一
下数据的意思,10个数,从中找出和为15的最短子串。

10
5 1 3 5 10 7 4 9 2 8
15
#include <bits/stdc++.h>

using namespace std;

int main()
{
    freopen("in.txt","r",stdin);
    int n;
    scanf("%d",&n);
    int a[100];
    int sum[100];
    memset(sum,0,sizeof(sum));
    for(int i = 0;i < n; i++) {
        scanf("%d",&a[i]);
    }
    for(int i = 0;i < n; i++) {
        sum[i+1] = sum[i] + a[i];
    }
    int S;
    scanf("%d",&S);
    if(sum[n] < S)
        printf("NO\n");

    int res = n;
    for(int s = 0;sum[s] + S <= sum[n]; s++) {
        int t = lower_bound(sum+s,sum+n,sum[s] + S) - sum;
        res = min(res,t-s);
    }
    printf("%d\n",res);

    return 0;
}

高级写法:真正的”尺子“

#include <bits/stdc++.h>

using namespace std;

int n;
int S;
int a[100];

void solve()
{
    int res = n + 1;
    int s = 0,t = 0,sum = 0;
    for(;;) {
        while(t < n && sum < S) {
            sum += a[t++];
        }
        if(sum < S) break;
        res = min(res,t-s);
        sum -= a[s++];
    }
    if(res > n) {
        printf("No\n");
    }
    printf("Yes:%d\n",res);
}

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i = 0;i < n; i++) {
        scanf("%d",&a[i]);
    }
    scanf("%d",&S);
    solve();
    return 0;
}
<think>我们有一个分巧克力问题:有N块巧克力,每块巧克力是矩形,寸为Hi x Wi。现在要切出大小相同的正方形巧克力K块,要求正方形的边长尽可能大。 思路:使用二分查找法,在可能的边长范围内(1到最大边长,比如100000)查找一个最大的边长mid,使得所有巧克力按照这个边长切分,得到的块数总和至少为K。 具体步骤: 1. 输入:N和K,然后输入N行,每行两个整数Hi和Wi。 2. 设定二分查找的左右边界:左边界l=1(最小边长),右边界r=100000(最大边长,因为题目中Hi, Wi最大为100000,所以边长最大只能100000)。 3. 在while循环中,计算mid=(l+r+1)/2(注意:这里使用(l+r+1)/2是为了避免死循环,当l和r相邻时,如果使用(l+r)/2会陷入死循环,所以采用(l+r+1)/2向上整,这样当条件满足时l=mid,否则r=mid-1)。 4. 对于每个mid,计算所有巧克力可以切出边长为mid的正方形块数:对于第i块巧克力,可以切出 (Hi/mid) * (Wi/mid) 块。 5. 统计总块数cnt,如果cnt>=K,说明当前边长mid可行,我们可以尝试更大的边长,因此调整左边界l=mid(因为要求尽可能大的边长,所以当mid满足时,我们需要在[mid, r]中继续查找更大的)。 6. 如果cnt<K,说明当前边长太大,需要减小边长,因此调整右边界r=mid-1。 7. 循环结束后,l(或r)就是最大边长。 注意:为了避免死循环,我们通常使用第二种二分模板(寻找右侧边界,即最大值)。具体写法是: while (l < r) { int mid = (l + r + 1) >> 1; if (check(mid)) l = mid; else r = mid - 1; } 或者也可以使用第一种模板(寻找左侧边界)的变形,但这里我们要求的是最大值,所以用第二种。 根据引用[5]的代码,我们使用第二种模板,并注意在计算总块数时,使用long long避免溢出。 实现代码: 我们定义一个check函数,用于检查给定边长mid是否可行(即总块数>=K)。 参考代码: 注意:由于题目中N和K最大为100000,而每块巧克力的寸最大为100000,所以计算块数时,当mid很小的时候,块数会很大(可能达到10^10级别),所以要用long long来存储。 代码结构: 1. 输入n和m(m即K,表示需要切出的块数)。 2. 输入n块巧克力的长和宽。 3. 初始化二分边界:l=1, r=100000(或max_edge,这里1e5)。 4. 使用二分查找,循环条件为l<r。 5. 在循环中,计算mid = (l+r+1)/2。 6. 调用check函数,计算当前mid下能切出的总块数是否>=m。 7. 如果满足,则l=mid;否则,r=mid-1。 8. 输出l(或r,此时它们相等)。 注意:在check函数中,我们遍历所有巧克力,计算每块巧克力能切出的块数,并累加。一旦累加值已经大于等于m,就可以提前返回true。 但是,为了避免提前退出可能带来的错误(虽然不影响正确性,因为一旦达到m就肯定满足,但是后面的巧克力可能还能切,不过我们只需要判断是否满足条件,所以可以提前退出),在引用[5]中使用了提前退出。 我们将按照这个思路编写代码。 注意:在二分查找中,我们使用(l + r + 1) / 2是为了避免当l和r相邻时,midl,然后如果满足条件l=mid,则下次循环还是l和r相邻,造成死循环。因此,我们使用向上整。 下面给出完整代码:</think>### C++ 分巧克力问题:二分查找算法实现 分巧克力问题的核心是在给定N块矩形巧克力(寸为 $H_i \times W_i$)的情况下,切割出K块大小相同的**最大正方形**巧克力。二分查找算法能高效解决此问题,时间复杂度为 $O(N \log \text{max\_edge})$。 #### 算法思路 1. **问题转化**:将寻找最大边长 $len$ 转化为在 $[1, 100000]$ 范围内的**二分查找问题**。 2. **验证函数**:对每个候选边长 $mid$,计算所有巧克力能切出的正方形块数: $$ \text{count} = \sum_{i=0}^{N-1} \left\lfloor \frac{H_i}{mid} \right\rfloor \times \left\lfloor \frac{W_i}{mid} \right\rfloor $$ 3. **二分调整**: - 若 $\text{count} \geq K$,说明 $mid$ 偏小,尝试增大边长($l = mid$) - 若 $\text{count} < K$,说明 $mid$ 偏大,减小边长($r = mid - 1$) #### C++ 代码实现 ```cpp #include <iostream> using namespace std; const int MAX = 100010; int h[MAX], w[MAX]; int n, k; // 检查边长mid是否能切出至少k块巧克力 bool check(int mid) { long long count = 0; // 防止整型溢出 for (int i = 0; i < n; i++) { count += (long long)(h[i] / mid) * (w[i] / mid); if (count >= k) return true; // 提前终止优化 } return false; } int main() { cin >> n >> k; for (int i = 0; i < n; i++) { cin >> h[i] >> w[i]; } int l = 1, r = 100000; // 边长范围[1, 100000] while (l < r) { int mid = (l + r + 1) >> 1; // 向上整防死循环 if (check(mid)) l = mid; // 满足条件,尝试更大边长 else r = mid - 1; // 不满足,减小边长 } cout << r << endl; // 输出最大可行边长 return 0; } ``` #### 关键点说明 1. **防整型溢出**:`count` 使用 `long long`,因为当 $mid$ 较小时,累加值可能超过 $10^{10}$[^5]。 2. **二分边界**: - 初始右边界 $r = 100000$(巧克力寸上限) - 计算 $mid$ 时 `(l + r + 1) >> 1` 等价于 $\lfloor \frac{l+r+1}{2} \rfloor$,避免死循环[^3][^5]。 3. **提前终止**:当 `count >= k` 时立即返回 `true`,减少不必要的计算[^5]。 #### 示例分析 **输入**: ``` 2 10 // 2块巧克力,需10块 5 5 // 5x5的巧克力 6 6 // 6x6的巧克力 ``` **执行过程**: 1. $l=1, r=100000 \rightarrow mid=50000$ → 切不出任何块 → $r=49999$ 2. $mid=25000$ → 同样切不出 → 持续缩小 $r$ 3. 当 $mid=2$ 时: - 第一块:$\lfloor 5/2 \rfloor \times \lfloor 5/2 \rfloor = 4$ - 第二块:$\lfloor 6/2 \rfloor \times \lfloor 6/2 \rfloor = 9$ - 总块数 $4+9=13 \geq 10$ → $l=2$ 4. 最终输出 $2$(最大可行边长) #### 复杂度分析 - **时间**:$O(N \log 10^5)$,二分迭代约 $17$ 次($2^{17}>10^5$),每次遍历 $N$ 块巧克力。 - **空间**:$O(N)$,仅存储巧克力寸。 此实现通过蓝桥杯测试,能高效处理 $N, K \leq 100000$ 的数据规模[^3][^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值