巨型多米诺骨牌

解题思路:

  1. 问题分析:

    • 我们需要选择一系列骨牌,从1号开始,到N号结束

    • 每个骨牌倒下时,必须能触发下一个骨牌倒下(即下一个骨牌大小 ≤ 2倍当前骨牌大小)

    • 目标是找到满足条件的最短骨牌序列

  2. 关键观察:

    • 必须包含第一个和最后一个骨牌

    • 中间的骨牌选择应该尽可能大,以减少需要的骨牌数量

    • 这是一个典型的贪心问题,每次选择满足条件的最大可能骨牌

  3. 算法设计:

    • 首先对骨牌大小进行排序,方便查找

    • 从第一个骨牌开始,每次寻找满足S_j ≤ 2*S_i的最大S_j

    • 使用二分查找来高效完成这个查找过程

    • 重复这个过程直到能够覆盖最后一个骨牌

  4. 复杂度分析:

    • 排序复杂度:O(n log n)

    • 每次查找复杂度:O(log n)

    • 最坏情况下需要O(n)次查找

    • 总复杂度:O(n log n) 每组测试用例

  5. 边界处理:

    • 如果无法找到满足条件的骨牌序列,返回-1

    • 特别注意当找到的骨牌就是当前骨牌本身的情况(无法扩展)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;  // 定义最大数组大小
int n, a[N];  // n表示骨牌数量,a数组存储骨牌大小

// 二分查找函数:在排序后的a数组中查找小于等于x的最大值的位置
int find(int x) {
    int l = 1, r = n, res = -1;  // 初始化左右指针和结果
    while(l <= r) {
        int mid = (l + r) >> 1;  // 计算中间位置
        if(a[mid] <= x) {        // 如果中间值小于等于x
            res = mid;           // 更新结果为当前位置
            l = mid + 1;         // 向右继续查找更大的值
        }
        else r = mid - 1;        // 否则向左查找
    }
    return res;                  // 返回找到的位置(找不到返回-1)
}

// 解决单个测试用例的函数
void solve() {
    cin >> n;  // 读取骨牌数量
    for(int i = 1; i <= n; i++) cin >> a[i];  // 读取骨牌大小
    
    int s = a[1], e = a[n];  // s是第一个骨牌大小,e是最后一个骨牌大小
    sort(a + 1, a + 1 + n);  // 对骨牌大小进行排序
    
    int ans = 2;  // 初始答案至少需要2个骨牌(第一个和最后一个)
    
    // 核心算法:贪心+二分
    while(2 * s < e) {  // 当当前最大值的2倍还小于最后一个骨牌大小时
        int pos = find(2 * s);  // 找到小于等于2*s的最大骨牌位置
        
        // 如果找不到或者找到的就是当前s本身(无法扩展)
        if(pos == -1 || a[pos] == s) {
            cout << -1 << endl;  // 输出无解
            return;
        }
        
        s = a[pos];  // 更新当前最大值为找到的骨牌大小
        ans++;       // 增加需要的骨牌数量
        
        if(s == e) break;  // 如果已经到达最后一个骨牌,提前退出
    }
    cout << ans << endl;  // 输出最少需要的骨牌数量
}

int main() {
    int T; cin >> T;  // 读取测试用例数量
    while(T--) {      // 处理每个测试用例
        solve();
    }
    return 0;
}

 

posted @ 2025-06-29 12:35  CRt0729  阅读(18)  评论(0)    收藏  举报