说明
zjn 有 n 块积木,每块积木的高度都相同,但是底面面积不同,第 ii块积木的底面面积为 wi
现在 zjn 用这些积木来搭一个 k层高的高塔,每一层只用一块积木,而为了高塔的稳定,对于相邻两层的积木来说,下层积木的底面面积必须是上层积木的两倍以上
现在 zjn 想知道,自己最多能搭几个 kk 层的高塔?
输入格式
第一行两个正整数 n,k,分别表示积木的块数,以及高塔的层数。
第二行 n 个正整数 w,分别表示每块积木的底面面积。
对于 30%的数据满足:n,k≤7
对于 100%的数据满足:2≤n≤2∗105,2≤k≤30,k≤n,1≤wi≤231
输出格式
输出一行一个整数,表示最多可搭建的高塔的个数。
样例
输入数据 1
9 3
6 1 11 4 8 7 2 18 3
输出数据 1
2
提示
其中一组方案,两个高塔分别是:{1 2 41 2 4},{3 7 183 7 18}
积木高塔问题思路
问题分析
我们需要用n块积木搭建尽可能多的k层高塔。每层使用一块积木,且相邻两层中,下层积木的底面面积必须至少是上层积木的两倍。目标是最大化高塔的数量。
算法选择
采用二分答案结合贪心匹配的策略:
-
二分答案:因为高塔数量t满足单调性(如果t可行,则小于t的值都可行;如果t不可行,则大于t的值都不可行)。t的可能范围是0到⌊n/k⌋。
-
贪心匹配:对于每个候选t,检查是否能搭建t个高塔。通过排序积木和贪心选择,确保每次使用最小的满足条件的积木,以最大化塔数。
步骤详解
-
排序积木:将积木按底面面积从小到大排序。这便于后续贪心匹配,因为小面积积木更适合作为顶层,大面积积木更适合作为底层。
-
二分查找t:
-
初始化左边界left = 0,右边界right = n // k。
-
当left ≤ right时:
-
计算中间值mid = (left + right) // 2。
-
检查是否能搭建mid个k层高塔(调用检查函数)。
-
如果可行,则尝试更大的t(left = mid + 1);否则,尝试更小的t(right = mid - 1)。
-
-
最终right是最大可行t。
-
-
检查函数(判断t是否可行):
-
如果t=0,直接返回true。
-
初始化t个塔:选择前t个最小的积木作为每个塔的顶层(因为顶层面积小,更容易满足下层面积大的要求)。
-
逐层构建:对于第2层到第k层:
-
将t个塔的当前顶层面积(即上一层的面积)排序,以便贪心匹配。
-
从剩余积木中,为每个塔分配一个最小的满足条件(积木面积 ≥ 2 × 当前顶层面积)的积木。如果找不到足够的积木,则t不可行。
-
成功分配后,更新每个塔的当前层积木。
-
-
如果所有层都分配成功,则t可行。
-
复杂度分析
-
时间复杂度:排序积木O(n log n)。二分查找O(log(n/k)),每次检查需要O(k × n)(因为每层需要遍历积木,k很小)。总复杂度O(n log n + k n log(n/k)),在n≤200,000和k≤30时可行。
-
空间复杂度:O(n)用于存储积木和临时塔信息。
关键点
-
贪心选择:每次为塔分配最小的满足条件的积木,以保留大面积积木用于后续层或其他塔,从而最大化塔数。
-
排序优化:积木排序后,贪心匹配可以高效进行,避免重复扫描。
这种思路确保了在大规模数据下高效求解,直接聚焦问题本质。
代码样例
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int n,k;
ll w[N];
bool check(int t)
{
if(t==0) return true;
vector<ll> v(t);
for(int i=0; i<t; i++) v[i]=w[i];
int j=t;
for(int l=2; l<=k; l++)
{
sort(v.begin(),v.end());
int cnt=0;
for(int i=0; i<t; i++)
{
while(j<n&&w[j]<2*v[i]) j++;
if(j<n)
{
v[i]=w[j];
j++;
cnt++;
}
else break;
}
if(cnt<t) return false;
}
return true;
}
int main()
{
cin>>n>>k;
for(int i=0; i<n; i++) cin>>w[i];
sort(w,w+n);
int l=0,r=n/k,ans=0;
while(l<=r)
{
int mid=l+(r-l)/2;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}
此代码仅供参考,请勿纯抄
327

被折叠的 条评论
为什么被折叠?



