题解 P1843 【奶牛晒衣服】

先奉上代码--本人的手打大根堆。
头文件需要 (也可以不要,把构造函数删了就是)。
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 几句:除了打表的,我这应该算是很快的。这个题解花的时间比我打正解花的时间久

人生第二篇题解~依然没过~

你可能感兴趣的:(题解 P1843 【奶牛晒衣服】)