三分法

orz hhw posted @ 2015年8月08日 14:52 in 算法学习 with tags 模板 bzoj 三分 算法学习 , 2699 阅读

这么简单的算法却一直不会,真是不太应该。。

BZOJ2330 一道三分裸题,但因为姿势不太对调了挺久,然后学会了保留有效位数输出小数,只要开<iomanip>库再cout<<setprecision(m)<<f<<endl就行啦,不过这道题似乎仍然不太资磁

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
int n,m,i;double ans,eps,a[25],b[25];
double cal2(double nd,double sl){
	double res=0;  
    for(int i=1;i<=n;i++){
        double s=100/(1+exp(nd-sl*a[i]));  
        if(s<1e-12||s>=100)return 1e200;  
        res+=b[i]*log(100/s)+(100-b[i])*log(100/(100-s));  
    }
    return res;
}
double cal(double nd){
	double l=0,r=1,mid1,mid2;
	while(r-l>eps){
		mid1=(l+r)/2;mid2=(mid1+r)/2;
		if(cal2(nd,mid1)>cal2(nd,mid2))l=mid1;else r=mid2;
	}
	return cal2(nd,(l+r)/2);
}
double print(double ans){
    long long a=(long long)ans;  
    int digit=0;  for(int tmp=a;tmp;tmp/=10)digit++;  
    if(m<=digit){
        int bit[100];for(*bit=0;a;a/=10)bit[++*bit]=a%10;  
        for(int i=*bit;i>=1 &&m;i--,digit--,m--)  putchar(bit[i]+'0');  
        while(digit--)putchar('0');  
    }else{
        cout<<a<<'.';m-=digit;
        while(m--)ans-=(int)ans,ans*=10,printf("%d",(int)ans);  
    }  
}
int main(){
	scanf("%d%d",&n,&m);
	eps=1.0;for(i=1;i<=m;i++)eps/=10.0;
	for(i=1;i<=n;i++)scanf("%lf",&a[i]);sort(a+1,a+n+1);
	for(i=1;i<=n;i++)b[i]=(i-1)*100.0/(n-1);
	double l=0.0,r=10.0,mid1,mid2;
	while(r-l>eps){
		 mid1=(l+r)/2;mid2=(mid1+r)/2;
		 if(cal(mid1)>cal(mid2))l=mid1;else r=mid2;
	}
	print(cal((l+r)/2));
}

BZOJ1857 更裸的三分,不过因为R变量用重复了也调了好久。。

#include<cstdio>
#include<cmath>
#define eps 1e-8
double ax,ay,bx,by,cx,cy,dx,dy,ex,ey,P,Q,R;
double dis(double x1,double y1,double x2,double y2){return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));}
double cal(double a,double b){
	double l=0,r=1,mid1,mid2,x,y,ret1,ret2;
	while(r-l>eps){
		mid1=(l+r)/2;mid2=(mid1+r)/2;
		x=dx+(cx-dx)*mid1;y=dy+(cy-dy)*mid1;
		ret1=dis(a,b,ax,ay)/P+dis(a,b,x,y)/R+dis(x,y,dx,dy)/Q;
		x=dx+(cx-dx)*mid2;y=dy+(cy-dy)*mid2;
		ret2=dis(a,b,ax,ay)/P+dis(a,b,x,y)/R+dis(x,y,dx,dy)/Q;
		if(ret1>ret2)l=mid1;else r=mid2;
	}
	mid1=(l+r)/2;x=dx+(cx-dx)*mid1;y=dy+(cy-dy)*mid1;
	return dis(a,b,ax,ay)/P+dis(a,b,x,y)/R+dis(x,y,dx,dy)/Q;
}
int main(){
	scanf("%lf%lf%lf%lf",&ax,&ay,&bx,&by);ex=dis(ax,ay,bx,by);
	scanf("%lf%lf%lf%lf",&cx,&cy,&dx,&dy);ey=dis(cx,cy,dx,dy);
	scanf("%lf%lf%lf",&P,&Q,&R);
	double l=0,r=1,mid1,mid2,x,y,ret1,ret2;
	while(r-l>eps){
		mid1=(l+r)/2;mid2=(mid1+r)/2;
		x=ax+(bx-ax)*mid1;y=ay+(by-ay)*mid1;
		ret1=cal(x,y);
		x=ax+(bx-ax)*mid2;y=ay+(by-ay)*mid2;
		ret2=cal(x,y);
		if(ret1>ret2)l=mid1;else r=mid2;
	}
	mid1=(l+r)/2;x=ax+(bx-ax)*mid1;y=ay+(by-ay)*mid1;
	printf("%.2lf",cal(x,y));
}

BZOJ3874 单调性贪心+三分,因为一个LL没开调了好久。。

#include<cstdio>
#include<algorithm>
#define N 333
using namespace std;
typedef long long LL;
LL m,f,n,i,top,midl,midr,l,r,ans,ansl,ansr;
struct EG{LL x,y;}eg[N],q[N];
bool cmp(EG a,EG b){return a.x<b.x;}
LL calc(LL x){
	LL xm,ans=0,days=0,la,i;
	xm=m-x*f;if(xm<0)return 0;
	for(i=1;i<=top;i++){
		if(days<=q[i].y){
			la=min(q[i].y-days+(LL)1,xm/q[i].x/x);
			ans+=la*x;xm-=q[i].x*x*la;
			days+=la;
		}
		if(days<=q[i].y){
			la=min(x,xm/q[i].x);
			ans+=la;xm-=q[i].x*la;days++;
		}
	}
	return ans;
}
int main(){
	scanf("%lld%lld%lld",&m,&f,&n);
	for(i=1;i<=n;i++)scanf("%lld%lld",&eg[i].x,&eg[i].y);
	sort(eg+1,eg+n+1,cmp);
	q[++top]=eg[1];
	for(i=2;i<=n;i++)if(eg[i].y>q[top].y)q[++top]=eg[i];
	l=1;r=m/(f+q[1].x);
	ans=max(calc(l),calc(r));
	while(l<=r){
		midl=l+(r-l)/(LL)3;midr=r-(r-l)/(LL)3;
		ansl=calc(midl);ansr=calc(midr);
		ans=max(ans,max(ansl,ansr));
		if(ansl<ansr)l=midl+1;else r=midr-1;
	}
	printf("%lld\n",ans);
}

BZOJ3203 很明显就是求max{sum[i]/d[i]},由于sum[i]递增且每次相对位置不变,相当于求1~i中到点(sum[i],d*i+y)的最大斜率。可以维护一个单调队列,然后在上面三分得到答案

#include<bits/stdc++.h>
int n,i,l,r,p,t,m1,m2;double x,y,v,d,w,ans;struct Q{double x,y;}q[100100],o;
Q operator-(Q a,Q b){Q t;t.x=a.x-b.x;t.y=a.y-b.y;return t;}
double operator*(Q a,Q b){return a.x*b.y-a.y*b.x;}
double xl(Q a,Q b){return (b.y-a.y)/(b.x-a.x);}
int main(){
	for(scanf("%d%lf",&n,&d),i=1;i<=n;i++){
		for(scanf("%lf%lf",&x,&y),o=Q{d*i,v},v+=x;t>1&&(o-q[t])*(q[t]-q[t-1])>=0;)t--;
		for(w=0,q[++t]=o,o=Q{y+d*i,v},l=1,r=t;r-l>2;){
			p=(r-l+1)/3;m1=l+p;m2=r-p;
			if(xl(q[m1],o)<xl(q[m2],o))l=m1;else r=m2;
		}
		for(p=l;p<=r;p++)w=std::max(w,xl(q[p],o));ans+=w;
	}
	printf("%.0f",ans);
}

BZOJ4311 按时间建立线段树,对询问离线拉链,每个点求出凸壳后每个询问三分找答案即可

#include<bits/stdc++.h>
#define N 200200
using namespace std;typedef long long LL;
int n,m,i,x,y,t,o,tot,fir[N*3],ne[3333333],la[3333333];LL ans[N];
struct P{int x,y;}a[N],b[N],c[N],p[N],q[N];bool cmp(P a,P b){return a.x<b.x||a.x==b.x&&a.y>b.y;}
void ins(int k,int l,int r,int x,int y,int z){
	if(x<=l&&r<=y){la[++tot]=z;ne[tot]=fir[k];fir[k]=tot;return;}
	int mid=l+r>>1;if(x<=mid)ins(k<<1,l,mid,x,y,z);
	if(y>mid)ins(k<<1|1,mid+1,r,x,y,z);
}
void ask(int x){
	int l,r,w,m1,m2;LL s1,s2;
	for(l=1,r=t;l<=r;){
		w=(r-l)/3,m1=l+w,m2=r-w;s1=1ll*c[x].x*q[m1].x+1ll*c[x].y*q[m1].y;s2=1ll*c[x].x*q[m2].x+1ll*c[x].y*q[m2].y;
		if(s1>s2)r=m2-1,ans[x]=max(ans[x],s1);else l=m1+1,ans[x]=max(ans[x],s2);
	}
}
void fz(int k,int l,int r){
	int i,w=0,mid=l+r>>1;if(l<r)fz(k<<1,l,mid),fz(k<<1|1,mid+1,r);
	for(i=fir[k];i;i=ne[i])p[++w]=a[la[i]];if(!w)return;sort(p+1,p+w+1,cmp);
	for(q[t=1]=p[1],i=2;i<=w;q[++t]=p[i++])for(;t>1&&1ll*(q[t].y-q[t-1].y)*(p[i].x-q[t].x)<=1ll*(p[i].y-q[t].y)*(q[t].x-q[t-1].x);t--);
	for(i=l;i<=r;i++)if(c[i].x)ask(i);
}
int main(){
	for(scanf("%d",&n),i=1;i<=n;i++){
		scanf("%d%d",&o,&x);
		if(o==1)scanf("%d",&y),a[++m]=P{x,y},b[m]=P{i,n};
		if(o==2)b[x].y=i;
		if(o==3)scanf("%d",&y),c[i]=P{x,y};
	}
	for(i=1;i<=m;i++)ins(1,1,n,b[i].x,b[i].y,i);
	for(fz(1,1,n),i=1;i<=n;i++)if(c[i].x)printf("%lld\n",ans[i]);
}
Avatar_small
q234rty 说:
2015年9月14日 12:15

其实应该是
cout<<fixed<<setprecision(m)<<f<<endl;

Avatar_small
Orz 说:
2016年5月16日 00:57

第一题是bzoj 3330吧……


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter