先奉上代码--本人的手打大根堆。
头文件需要 (也可以不要,把构造函数删了就是)。
struct Heap
{
int heap[N + 42],Size;//这里的 N 是宏定义的
Heap(){memset(heap,0,sizeof(heap));Size = 0;}// 构造函数
inline void Insert (const int key)
{
heap[++Size] = key;
int father,i = Size;
while((i >> 1)){
father = i >> 1;
if(heap[i] < heap[father]) break;
heap[i] ^= heap[father] ^= heap[i] ^= heap[father];
i = father;
}
}
inline void maintain(const int increment)
{
int son,i = 1;heap[1] -= increment;
while((i << 1) <= Size){
son = i << 1;
if((son | 1) <= Size && heap[son | 1] > heap[son]) son |= 1;
if(heap[i] > heap[son]) break;
heap[i] ^= heap[son] ^= heap[i] ^= heap[son];
i = son;
}
}
inline int Get()
{
if(Size == 0) return -1;
else if(Size == 1) return heap[Size--];
heap[1] ^= heap[Size] ^= heap[1] ^= heap[Size];--Size;
int son,i = 1;
while((i << 1) <= Size){
son = i << 1;
if((son | 1) <= Size && heap[son | 1] > heap[son]) son |= 1;
if(heap[i] > heap[son]) break;
heap[i] ^= heap[son] ^= heap[i] ^= heap[son];
i = son;
}
return heap[Size + 1];
}
} H;
然后让我们来看一下这个堆支持的基本操作:
插入
inline void Insert (const int key)//inline 是一个小优化
{
heap[++Size] = key;//直接插入到尾部
int father,i = Size;
while((i >> 1)){//向上调整
father = i >> 1;
if(heap[i] < heap[father]) break;//大根堆,父亲大于儿子
heap[i] ^= heap[father] ^= heap[i] ^= heap[father];//这是一种整形变量交换的方法
i = father;//儿子比父亲大,所以交换位置
}
}
\(<<\) , \(>>\) 是移位操作
其中 \(a << i\) 相当于 $a \times 2^i $ 。
\(a >> i\) 则相当于 \(a \times 2^{-i}\)。
举例:
1 转换成二进制是 \(1_{{(000001)}_2}\) ,而 \(1 << 3\) 就是变为 \({(001000)}_2\) 即 \(2^3 = 8_{{(001000)}_2}\) ,正好移了三位
移位操作不支持负数,浮点数等,它的速度比常规 \(\times\) ,\(\div\) 要快一点(它是基于二进制的操作)。
向上调整其实没什么好说的,注意不要越界就是了,时间复杂度 \(\Theta(log_2n)\)
堆顶元素改变后的维持操作
inline void maintain(const int increment)
{
int son,i = 1;heap[1] -= increment;
while((i << 1) <= Size){
son = i << 1;
if((son | 1) <= Size && heap[son | 1] > heap[son]) son |= 1;//往下就要注意要跟最大的儿子交换,所以如果存在右儿子,就要与之比较,取较大的。
if(heap[i] > heap[son]) break;
heap[i] ^= heap[son] ^= heap[i] ^= heap[son];
i = son;
}
}
\(i | 1\) 在代码中相当于 \(i + 1\)
原理则是基于移位操作,如:
\(1_{{(000001)}_2} << 1\) 变为 \(2_{{(000010)}_2}\) 再与 \(1_{{(000001)}_2}\) 进行或操作 \(_{{(000001)}_2}^{{(000010)}_2}\) 就变成\(3_{{(000011)}_2}\) (二进制位一一对应,只要有一个是 \(1\) 就置为 \(1\) ) 。
时间复杂度也是 \(\Theta(log_2n)\) 。
最后一个,取堆顶元素
inline int Get()
{
if(Size == 0) return -1;//堆是空的。
else if(Size == 1) return heap[Size--];//如果没有这句的话,当 Size = 1 时,会有问题。
heap[1] ^= heap[Size] ^= heap[1] ^= heap[Size];--Size;
int son,i = 1;
while((i << 1) <= Size){
son = i << 1;
if((son | 1) <= Size && heap[son | 1] > heap[son]) son |= 1;
if(heap[i] > heap[son]) break;
heap[i] ^= heap[son] ^= heap[i] ^= heap[son];
i = son;
}
return heap[Size + 1];//返回此前的堆首
}
跟维持操作没什么区别,稍微改一下就是的,时间复杂度也是 \(\Theta(log_2n)\)
写完堆来看题目
题目地址:洛谷 P1843
很容易就想到一个贪心的思路,模拟每一个小时,对湿度最大那件用烘衣机,烘完以后重新找当前湿度最大的使用,最后得到的肯定是最优的。
于是这道题便可以水(直接用 \(STL\) 也可以)过去了。
时间复杂度 \(\Theta(nlog_2n)\)
template
inline void in(T& x)
{
x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + (ch ^ 48);ch = getchar();}
}
快速读入模板
int main()
{
in(n);in(a);in(b);
for(reg int i = 1; i <= n; ++i) H.Insert(Read());
while(H.heap[1] > tim){tim += a;H.maintain(b);}
printf("%d",tim/a);
return 0;
}
用 \(tim\) 来累计当前自然风干的湿度,\(H.heap[1]\) 是目前湿度最大的衣服。
\(a\) 是自然风干速度, \(b\) 是烘衣机烘干速度。
最后输出 \(tim \div a\) 。
提交记录: 手打堆评测记录
小声 bb 几句:除了打表的,我这应该算是很快的。这个题解花的时间比我打正解花的时间久