hdu 3918 (Beiju)

题目大意:由两组从x轴向上延伸的相连线段(单调上升且互不相交)构成一个二维纸杯,其底为x轴,求其能装水的最大高度,这里不仅水不能溢出,而且还要考虑因重心与底的相对位置变化引起的翻到问题,即重心的x只能在底面的x左边范围之内、
解法:用一条与x轴平行的直线由底向上扫描,直到任意一段到达顶点或者重心x坐标超出,重心坐标超出的临界高度二分求出。

#include<cstdio>
#include<cmath>
#define eps 1e-8
const int maxn=105;
struct point{
    double x,y;
    point(double xx=0,double yy=0){ x=xx;y=yy;}
}left[maxn],right[maxn],list[5];
int dcmp(double k){
    return (k>eps)-(k<-eps);
}
double cross(point a,point b,point c){
    return (c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);
}
double area(point* p,int k){//多边形面积
    double s1=0,s2=0;
    for(int i=0;i<k;i++)
        s1+=p[(i+1)%k].y*p[i].x,s2+=p[(i+1)%k].y*p[(i+2)%k].x;
    return fabs(s1-s2)/2;
}
point intersection(point u1,point u2,point v1,point v2){//直线交点
    point ret=u1;
    double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
            /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
    ret.x+=(u2.x-u1.x)*t;
    ret.y+=(u2.y-u1.y)*t;
    return ret;
}
point gravity(point *p, int k){
double area = 0;
point center;
center.x = 0;
center.y = 0;

for (int i = 0; i < k-1; i++){
   area += (p[i].x*p[i+1].y - p[i+1].x*p[i].y)/2;
   center.x += (p[i].x*p[i+1].y - p[i+1].x*p[i].y) * (p[i].x + p[i+1].x);
   center.y += (p[i].x*p[i+1].y - p[i+1].x*p[i].y) * (p[i].y + p[i+1].y);
}

area += (p[k-1].x*p[0].y - p[0].x*p[k-1].y)/2;
center.x += (p[k-1].x * p[0].y - p[0].x * p[k-1].y) * (p[k-1].x + p[0].x);
center.y += (p[k-1].x * p[0].y - p[0].x * p[k-1].y) * (p[k-1].y + p[0].y);

center.x /= 6*area;
center.y /= 6*area;

return center;
}
int n,m;
point l_pre,r_pre,l_now,r_now;
double x_pre,area_pre,x_now,area_now,xx;
void init_now(){
    list[0]=l_pre;list[1]=r_pre;list[2]=r_now;list[3]=l_now;
    area_now=area(list,4);
    x_now=gravity(list,4).x;
    xx=(x_pre*area_pre+x_now*area_now)/(area_pre+area_now);
}
bool ok(double xx){
    return dcmp(xx-left[0].x)>=0&&dcmp(xx-right[0].x)<=0;
}
bool fun(double h){
    point p1=point(l_pre.x,l_pre.y+h);
    point p2=point(r_pre.x,r_pre.y+h);
    l_now=intersection(l_pre,l_now,p1,p2);
    r_now=intersection(r_pre,r_now,p1,p2);
    init_now();
    return ok(xx);
}
int ll,rr;
void solve(){
    ll=rr=0;
    area_pre=0;
    l_pre=left[0];r_pre=right[0];
    while(ll<n-1&&rr<m-1){
        int tl=ll+1,tr=rr+1;
        if(left[tl].y==right[tr].y){
            ll=tl;rr=tr;
            l_now=left[tl];r_now=right[tr];
        }else if(left[tl].y>right[tr].y){//左边高,取右边
            rr=tr;
            r_now=right[rr];
            l_now=intersection(left[ll],left[tl],r_now,point(r_now.x+1.0,r_now.y));
        }else{//右边高,取左边
            ll=tl;
            l_now=left[ll];
            r_now=intersection(right[rr],right[tr],l_now,point(l_now.x+1.0,l_now.y));
        }
        init_now();
        if(ok(xx)){
            l_pre=l_now;r_pre=r_now;
            x_pre=xx;area_pre+=area_now;
        }else{
            double low=0,high=l_now.y-l_pre.y,mid;
            while(low+1e-6<high){
                mid=(low+high)/2;
                if(fun(mid)) low=mid;
                else high=mid;
            }
            printf("%.3lf\n",l_pre.y+low);
            return;
        }
    }
    printf("%.3lf\n",l_pre.y);
}
int main(){
    int ca;
    scanf("%d",&ca);
    while(ca--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%lf%lf",&left[i].x,&left[i].y);
        for(int i=0;i<m;i++) scanf("%lf%lf",&right[i].x,&right[i].y);
        solve();
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值