巨型多米诺骨牌
解题思路:
- 
问题分析:
- 
我们需要选择一系列骨牌,从1号开始,到N号结束
 - 
每个骨牌倒下时,必须能触发下一个骨牌倒下(即下一个骨牌大小 ≤ 2倍当前骨牌大小)
 - 
目标是找到满足条件的最短骨牌序列
 
 - 
 - 
关键观察:
- 
必须包含第一个和最后一个骨牌
 - 
中间的骨牌选择应该尽可能大,以减少需要的骨牌数量
 - 
这是一个典型的贪心问题,每次选择满足条件的最大可能骨牌
 
 - 
 - 
算法设计:
- 
首先对骨牌大小进行排序,方便查找
 - 
从第一个骨牌开始,每次寻找满足S_j ≤ 2*S_i的最大S_j
 - 
使用二分查找来高效完成这个查找过程
 - 
重复这个过程直到能够覆盖最后一个骨牌
 
 - 
 - 
复杂度分析:
- 
排序复杂度:O(n log n)
 - 
每次查找复杂度:O(log n)
 - 
最坏情况下需要O(n)次查找
 - 
总复杂度:O(n log n) 每组测试用例
 
 - 
 - 
边界处理:
- 
如果无法找到满足条件的骨牌序列,返回-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; }
                    
                
                
            
        
浙公网安备 33010602011771号