【刷题】【二分】【三分】(2)

神奇的check函数,

任世界千变万化,我check永远是check

 

1>land

一棵树,求砍cut刀以后,
cut+1棵数中,直径最大值 最小是多少

//因为cut刀怎么砍与直径最大值有关,不好控制,
//所以我们通过枚举最大的直径,然后去dfs砍枝

//然后,让我们来见证,这个神奇的二分剪法 
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
int n,cut;
const int N=4e5+3;
int d[N],sz[N];
vector <int> g[N];

int mid,sum;
int check(int nw,int f)
{
    int l_mn=mid,r_mx=0,cnt=0; 
    
    for(int i=0;i<sz[nw];i++)
    {
        int u=g[nw][i];
        if(u==f) continue;
        
        int len=check(u,nw)+1;
        if(len>mid) sum++;
        else if(len>(mid>>1)) cnt++,l_mn=min(l_mn,len);
        else if(len>0) r_mx=max(r_mx,len);
    }
    
    if(cnt)
    {
        sum+=cnt;
        if(l_mn+r_mx<=mid)
        {
            sum--;
            return l_mn;
        }
    }
    return r_mx;
}

int main()
{
    scanf("%d%d",&n,&cut);
    int u,v;
    for(int i=1;i<n;i++)
        scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
    for(int i=1;i<=n;i++)
        sz[i]=g[i].size() ;
    
    if(cut+1>=n) 
        printf("0\n");
    else
    {
        int l=0,r=n,ans=0;
        while(l<=r)
        {
            mid=(l+r)>>1;//>cut就是这个解太优了,要把解变差一点,就是把ans的范围往右边调整 
            sum=0,check(1,0); 
if(sum>cut) l=mid+1; else ans=mid,r=mid-1;//重点注意这个地方的写法!!!! . //求的是cut刀,最小ans是多少, //但是有可能此最小ans的时候,最小砍cut-1刀, //所以写的时候,就是<=cut的时候,都更新答案,但是大于不行 } printf("%d\n",ans); } return 0; }

2>Best Cow Fences

 

整理一个知识点:

求最大子序列和

O(n)

单独考虑每个点,前面的最优解>0,就加上,如果<0,就不加,

再选自己,

如果自己加上最优解,都<0,就都别取了

 

给你一个正整数序列,找出一个区间使得平均值最大,要求该区间的长度大于等于L。 

策略:二分答案

//小数二分,令人头大 
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
int n,l;
const int N=1e5+3;
double d[N];
double dd[N],sum[N],f[N];

bool check(double mid)
{
    memset(sum,0,sizeof(sum));
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++) 
    {
        dd[i]=d[i]-mid;
        sum[i]=sum[i-1]+dd[i];
        f[i]=max(0.0,dd[i]+max(0.0,f[i-1]));
    }
    
    double ans=-1;
    for(int i=l;i<=n;i++)
        ans=max(ans,sum[i]-sum[i-l]+f[i-l]);
    return (ans>0);
}

int main()
{
    scanf("%d%d",&n,&l);
    double mn=2003,mx=0;
    for(int i=1;i<=n;i++) 
    {
        scanf("%lf",&d[i]);
        mn=min(mn,d[i]),mx=max(mx,d[i]);
    }
    
    int cnt=500;
    double l=mn,r=mx;
while(r-l>=0.00001 && --cnt)//注意,这里是二分小数型写法 { double mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } r*=10000; printf("%d\n",(int)r/10) ; //不四舍五入型写法 return 0; }

3>曲线

就一个二次函数形状的图像,二分找答案最小,

但因为x,y的取值是全实数,所以我二分x,y的时候,要多出来特别多的精度,

大概长这样:r-l>=0.00000001

然后计算复杂,加cnt算了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n;
const int N=1e5+3,inf=-2e9;
int a[N],b[N],c[N];

double f(double x)
{
    double ans=inf;
    for(int i=1;i<=n;i++)
        ans=max(ans,double(x*x*a[i] +x*b[i] +c[i] ) );
    return ans;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n); 
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&a[i],&b[i],&c[i]);
        double l=0,r=1000,as=0;
        int cnt=400; 
        while(r-l>=0.00000001)//就是精度不够 
        {
            double m1=l+(r-l)/3,m2=r-(r-l)/3;
            double v1=f(m1),v2=f(m2);
            if(v1<=v2) r=m2,as=v1;
            else l=m1,as=v2;
        } 
        printf("%.4f\n",as );
    }
    return 0;
} 

4>生日快乐

一看就是二分,但是是 二分+搜索+小数处理...

题目很好

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int x,y,k;
double s;

double dfs(double m,double n,int cut)
{
    if(cut==1)
        return (double)max(m,n)/min(m,n);
    
    double ans;
    double mx_m=s/n,mx_n=s/m;
    
    ans=dfs(mx_m,n,1);
    for(int i=2;i<cut;i++)
        ans=min(ans,max(dfs(mx_m*i,n,i) , dfs(m-mx_m*i,n,cut-i) ));
    for(int j=1;j<cut;j++)
        ans=min(ans,max(dfs(m,mx_n*j,j) , dfs(m,n-mx_n*j,cut-j) ));
    return ans;
}

int main()
{
    
    scanf("%d%d%d",&x,&y,&k);
    s=x*y*1.0/k;
    printf("%.6lf\n",dfs(x*1.0,y*1.0,k));
    
    return 0;
}

 

 

4>派

 

5>皇帝的烦恼

 

posted @ 2019-09-29 17:57  心若笺诗  阅读(129)  评论(0编辑  收藏  举报