组队赛11:2018黑龙江省赛H题/HDU - 6487 Overflow (思维+后缀和+计算)

题目

HDU - 6487
http://acm.hdu.edu.cn/showproblem.php?pid=6487
2018黑龙江省赛 H题Overflow

题意

一个桶里有水,给你很多个木块,都丢进去后,水的高度变为多少?

思路

其他人没考虑全面的做法:
没有考虑水量不够的情况,因为这道题的数据很水,也能直接过。例如这个:https://blog.csdn.net/kuronekonano/article/details/82821059#commentsedit
不过如果数据出的好,就过不了了。


我是这样做的:

首先看性质:
木块的密度分两种,大于等于水的,小于水的。
小于水的在水量充足的情况下会漂浮,在水量不够的情况下是沉底的。
密度大于等于水在水量充足的情况下会完全在水里,在水量不够的情况下是会露出来一部分的,所以要关注水量多少

该做法的主要部分是 木块等价底面积后缀和计算

先把木桶当做没有水的,然后所有的木块都丢进去,然后再加水
这里我们把木块等价变换一下
我们知道,密度比水大的木块,它能整个被淹掉。
而密度比水小的木块,会有一个临界点,就是浮力刚好等于重力的时候,我们算出这个情况下,水淹没了这个木块的高是多少(临界点的高度),将木块的高度更新为这个新的高度(被水淹没的高度)。因为密度比水小,不管你水加的再多,都不可能淹没超过这个高度,这么做就是把这个密度比水小的木块,等价成了一个密度比水大的木块,而这个新木块的高度为 原先密度比水小时 能被水淹没的最大高度(底面的边长不变)。
然后我们就把所有的木块按新高度从小到大排序,(放在结构体里,L是底面边长,nL是高度)。

木块等价后,我们就可以把所有的木块放到桶底,然后把水倒进去。

接下来算了一下木块的底面积后缀和,因为后面按高度分层计算要用到。

然后我们按高度分层计算。
现在看木块的高度最小的和桶底这一层,用 这一层的体积 减去 木块占据的那些部分的体积 后,看手中的水能不能够把剩下的空间填满。
可以的话就往更高一层看,看高度最小的木块和高度第二小的木块这一层。看剩下的水足够把空隙填满?足够就再往后面推(看高度第二小和高度第三小),如果水不够填满的话,说明水位就在当前层,我们拿剩下的水除以(底面积 - 高度后缀),就得出水在这一层的高度h,h再加上那个高度比较小的木块的高度,就得到水位了。

如果水量足够把所有木块都淹没,那么要考虑水是否会溢出水桶,溢出就等于桶的高度,否则就再加上桶顶与最高木块之间这一层的水位高度就好了。

这个做法就线性的跑了几次,时间复杂度为O(n)

整体流程是:
读数据,然后进行木块等价变换,接着按高度排序,再计算一下底面积后缀和,最后分层求解水位即可。

代码

#include <cstdlib>
#include <cstdio> 
#include <cstring>
#include <cmath>
#include <algorithm> 
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
double L[maxn];//木块边长
double P[maxn];//密度
double nL[maxn];//等价木块后的高度
double suf[maxn];//底面积后缀和
struct node{
	double nL;
	double L;
}len[maxn];
int cmp(node a,node b)
{
	return a.nL < b.nL;
}
int main()
{	
	int t;
	scanf("%d",&t);
	while(t--)//10
	{
		int n;
		scanf("%d",&n);//1e4
		for(int i = 1;i <= n;i++)
			scanf("%lf %lf",&L[i],&P[i]);
			
		double S,H,V;
		scanf("%lf %lf %lf",&S,&H,&V);
		
		for(int i = 1;i <= n;i++)
		{
			if(P[i] < 1)//把会漂浮起来的木块等价成不会漂浮的木块 
				nL[i] = P[i]*L[i];//等价成的木块的新的高度,底面边长不变 。其实是等于P*(L的三次方)再除以 L的二次方,化简后就是P*L了
			else
				nL[i] = L[i];
		}
		for(int i=1;i<=n;i++)//一开始忘记开结构体了,排序后底边和高对不上号,这里修改的时候图快,就这样改了,这里可以优化。
		{
			len[i].nL = nL[i];
			len[i].L = L[i];
		}
		sort(len + 1,len + 1 + n,cmp);
		
		suf[n] = len[n].L * len[n].L;//底面积 
		for(int i = n - 1;i >= 1;i--)
			suf[i] = suf[i+1] + len[i].L * len[i].L;//底面积后缀和 
		
		double ans = 0;
		len[0].nL = 0;
		len[0].L = 0;
		for(int i = 1;i <= n;i++)
		{
			if(len[i].nL == len[i-1].nL)//高度相等
				continue;
			else//高度不相等
			{
				V -= S * (len[i].nL - len[i-1].nL)  - (len[i].nL - len[i-1].nL) * suf[i];
//S * (len[i].nL - len[i-1].nL)  - (len[i].nL - len[i-1].nL) * suf[i]为水把这一个高度差里面的空隙用水填满需要的水的体积
				if(V < 0)//如果水不够填满这一层除了木块占用部分以外的空间
				{
					V += S * (len[i].nL - len[i-1].nL)  - (len[i].nL - len[i-1].nL) * suf[i];//不够填满,再加回去
					ans += V / ( S - suf[i] ) ;//算出这一层的高度,ans加上
					V = 0;//水都用完了
					break;
				}
				else//剩余的V>=0,足够填满当前层除了木块占用部分以外的空间
					ans = len[i].nL;
			}
		}
		if(V > 0)//如果淹没所有的木块后还剩水,看剩下的水有没有溢出水桶
		{
			if(V < (H - len[n].nL) * S)
				ans += V / S; 
			else
				ans = H;
		}
		printf("%.2lf\n",ans);
	}
	return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值