• 二分


    二分

      二分有两种:二分答案和二分查找;一般来说二分查找没什么意思,主要还是二分答案。

      聪明的质检员:https://www.luogu.org/problemnew/show/P1314

      题意概述:

      题目给定了n个矿石,有价值和重量,m个区间$[L_i,R_i]$,自选一个参数W,对于每个区间求校验值并相加,为总校验值。求一个最优的$W$使得总校验值与标准值(给定)最接近。

      如何计算校验值?区间内质量超过W的矿石数量与这些矿石的价值之和的乘积。

      首先呢...W越大,满足条件的矿石数量就越多,价值之和就越大,校验值自然也越大啦,所以可以二分。因为我二分的知识十分匮乏...所以想了一个比较适合二分蒟蒻的做法:首先二分一个最大的小于标准值的校验值,再二分一个最小的大于标准值的校验值,取一个min。二分答案之后怎么check呢?用前缀和就好了。时间复杂度$O(log(maxw)(n+m))$

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # define R register int
     5 
     6 using namespace std;
     7 
     8 const int maxn=200005;
     9 int n,m,l,r;
    10 long long s,ans,t,S[maxn],cnt[maxn];
    11 int w[maxn],v[maxn],maxw,minw,mid;
    12 struct nod
    13 {
    14     int l,r;
    15 }a[maxn];
    16 
    17 int read()
    18 {
    19     int x=0,f=1;
    20     char c=getchar();
    21     while (!isdigit(c))
    22     {
    23         if(c=='-') f=-f;
    24         c=getchar();
    25     }
    26     while (isdigit(c))
    27     {
    28         x=(x<<3)+(x<<1)+(c^48);
    29         c=getchar();
    30     }
    31     return x*f;
    32 }
    33 
    34 long long ab (long long a)
    35 {
    36     if(a<0) return -a;
    37     return a;
    38 }
    39 
    40 long long check (int W)
    41 {
    42     memset(S,0,sizeof(S));
    43     memset(cnt,0,sizeof(cnt));
    44     for (int i=1;i<=n;++i)
    45     {
    46         if(w[i]>=W) S[i]=v[i],cnt[i]=1;
    47         S[i]+=S[i-1];
    48         cnt[i]+=cnt[i-1];
    49     }
    50     long long ans=0;
    51     for (int i=1;i<=m;++i)
    52         ans+=(S[ a[i].r ]-S[ a[i].l-1 ])*(cnt[ a[i].r ]-cnt[ a[i].l-1 ]);
    53     return ans;
    54 }
    55 
    56 int main()
    57 {
    58     scanf("%d%d%lld",&n,&m,&s);
    59     w[1]=read(),v[1]=read();
    60     maxw=minw=w[1];
    61     for (R i=2;i<=n;i++)
    62         w[i]=read(),v[i]=read(),maxw=max(maxw,w[i]),minw=min(minw,w[i]);
    63     for (R i=1;i<=m;i++)
    64         a[i].l=read(),a[i].r=read();
    65     l=minw,r=maxw;
    66     ans=check(l);
    67     while (l<=r)
    68     {
    69         mid=(l+r)>>1;
    70         t=check(mid);
    71         if(t<s) r=mid-1;
    72         else { l=mid+1; ans=min(ans,ab(t-s)); }
    73     }
    74     l=minw,r=maxw;
    75     while (l<=r)
    76     {
    77         mid=(l+r)>>1;
    78         t=check(mid);
    79         if(t>s) l=mid+1;
    80         else { r=mid-1; ans=min(ans,ab(t-s)); };
    81     }
    82     printf("%lld",ans);
    83     return 0;
    84 }
    聪明的质检员

      Best Cow Fences:https://loj.ac/problem/10012

      题意概述:给定一个长度为$n$的非负整数序列$A$,找出一个平均数最大的,长度不小于$L$的子段,输出这个平均值。

      这道题乍一看没有什么思路,仔细想一下发现是一道$0-1$分数规划;

      $0-1$分数规划这个算法我没有专门学过,类似于一种二分化式子的思想?

      这道题的答案是满足单调性的,显然平均数大于$2$肯定更大于$1.$二分之后$check$:把所有数字都减去这个平均值,那么如果一段的和是整数就说明它的平均值大于等于二分值.找长度大于等于$L$的最大子段和就很简单啦.

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # include <string>
     5 # include <algorithm>
     6 # include <cmath>
     7 # define R register int
     8 # define ll long long
     9 
    10 using namespace std;
    11 
    12 const int maxn=100005;
    13 int n,L,l,r,mid,ans;
    14 int mi,ma,a[maxn];
    15 ll s[maxn];
    16 
    17 bool check (int x)
    18 {
    19     ll minn=0;
    20     for (R i=1;i<=n;++i)
    21         s[i]=s[i-1]+a[i]-x;
    22     for (R i=L;i<=n;++i)
    23     {
    24         if(i-L>=0) minn=min(minn,s[i-L]);
    25         if(s[i]>=minn) return true;
    26     }
    27     return false;
    28 }
    29 
    30 int main()
    31 {
    32     scanf("%d%d",&n,&L);
    33     scanf("%d",&a[1]);
    34     a[1]*=1000;
    35     mi=ma=a[1];
    36     for (R i=2;i<=n;++i)
    37         scanf("%d",&a[i]),a[i]*=1000,mi=min(mi,a[i]),ma=max(ma,a[i]);
    38     l=mi,r=ma;
    39     while(l<=r)
    40     {
    41         mid=(l+r)>>1;
    42         if(check(mid))
    43             ans=mid,l=mid+1;
    44         else
    45             r=mid-1;
    46     }
    47     printf("%d",ans);
    48     return 0;
    49 }
    Best Cow Fences

      最小圈:https://www.lydsy.com/JudgeOnline/problem.php?id=1486

      题意概述:在一个有向图中找一个平均值最小的环.

      说实话这道题我一直不大想写,因为它实在是有点假.嗯,非常假.太假了!

      和上边那个题其实差不多的,都是$0-1$分数规划;

      二分答案之后全部减掉,然后找负环.找负环.找负环.这道题网上的大多数题解竟然都是拿$dfs-spfa$过的!而且只要用$spfa$这算个什么题目.

      不过这题也是有理论正解的,偷偷贴一个$rqy$的博客好了:https://www.cnblogs.com/y-clever/p/7043553.html;

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <queue>
     5 # include <cstring>
     6 # include <string>
     7 # define pac make_pair
     8 # define R register int
     9 # define ll long long
    10 
    11 using namespace std;
    12 
    13 const double eps=0.000000001;
    14 const int maxn=3003;
    15 const int maxm=10004;
    16 int h,n,m,firs[maxn],x,y,in_que[maxn],vis[maxn];
    17 double l=-1,r,mid,ans,d[maxn],z;
    18 bool f=false;
    19 struct edge
    20 {
    21     int too,nex;
    22     double co;
    23 }g[maxm+maxn];
    24 
    25 void add (int x,int y,double z)
    26 {
    27     g[++h].too=y;
    28     g[h].nex=firs[x];
    29     firs[x]=h;
    30     g[h].co=z;
    31 }
    32 
    33 void dfs (int x,double k)
    34 {
    35     vis[x]=true;
    36     int j;
    37     for (R i=firs[x];i;i=g[i].nex)
    38     {
    39         j=g[i].too;
    40         if(d[x]+g[i].co-k<d[j])
    41         {
    42             if(f) return;
    43             if(vis[j])
    44             {
    45                 f=true;
    46                 return ;
    47             }
    48             d[j]=d[x]+g[i].co-k;
    49             dfs(j,k);
    50         }
    51     }
    52     vis[x]=false;
    53 }
    54 
    55 bool check (double k)
    56 {
    57     memset(d,0,sizeof(d));
    58     memset(vis,0,sizeof(vis));
    59     d[0]=0;
    60     f=false;
    61     dfs(0,k);
    62     return f;
    63 }
    64 
    65 int main()
    66 {
    67     scanf("%d%d",&n,&m);
    68     for (R i=1;i<=m;++i)
    69     {
    70         scanf("%d%d%lf",&x,&y,&z);
    71         add(x,y,z);
    72     }
    73     for (R i=1;i<=n;++i)
    74         add(0,i,0);
    75     r=1e5,l=-1e5;
    76     ans=r;
    77     while (r-l>=-eps)
    78     {
    79         mid=(l+r)/2.0;
    80         if(check(mid))
    81             ans=min(ans,mid),r=mid-eps;
    82         else
    83             l=mid+eps;
    84     }
    85     printf("%.8lf",ans);
    86     return 0;
    87 }
    最小圈

      

  • 相关阅读:
    WIN7 系统 右键计算机 点击管理 出现对话框:找不到文件。
    电脑优化的方法
    小问题总结
    sql server常用函数、常用语句
    java绝对路径和相对路径的理解
    日常开发常用网站(持续更新……)
    jquery字符串序列化方法总结
    J2EE保留小数问题
    error C2556: 'const char &MyString::operator [](int)' : overloaded function differs only by return type from 'char &MyString::operator [](int)'
    有个人愿意对你微笑,如果她的眼神是坚定的,她是谁对我其实已经不重要了
  • 原文地址:https://www.cnblogs.com/shzr/p/9526463.html
Copyright © 2020-2023  润新知