二分

二分

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

  聪明的质检员: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 }
最小圈

 

  

posted @ 2018-08-23 21:24  shzr  阅读(370)  评论(0编辑  收藏  举报