zoukankan      html  css  js  c++  java
  • 二分(答案)

    二分(答案)

    什么情况下用二分?两个条件:上下界[a, b]确定、函数在[a, b]内单调

    整数二分

    STL的lower_bound()和upper_bound()

    通往奥格瑞玛的道路

    最大值最小化

    对点权fi进行二分(先排序),用dijkstra求最短路,检验总边权是否小于b。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long LL;
    const int N=510005;
    const int inf=10000000000;
    inline LL read() {
    	LL x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,s,t;
    int hd[N],to[N],nxt[N],tot=1;
    LL w[N],dis[N],b;
    inline void add(int x,int y,LL z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    LL cost[N],f[N];
    bool vis[N];
    bool check(LL val) {
    	if(val<cost[1]||val<cost[n]) return 0;
    	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
    	priority_queue< pair<LL,int> >q;
    	q.push(make_pair(0,1));
    	dis[1]=0;
    	while(!q.empty()) {
    		int x=q.top().second;q.pop();
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(cost[y]>val||vis[y]) continue;
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    //				cout<<y<<" "<<dis[y]<<endl;
    				if(!vis[y]) 				
    					q.push(make_pair(-dis[y],y));
    			}
    //			if(y==n) return dis[n]<=b;
    		}	
    	}
    	return dis[n]<=b;
    }
    
    int main() {
    	n=read();m=read();b=read();
    	for(int i=1;i<=n;i++) cost[i]=f[i]=read();
    	sort(f+1,f+1+n);
    	int x,y;LL z;
    	for(int i=1;i<=m;i++) {
    		x=read();y=read();z=read();
    		add(x,y,z),add(y,x,z);
    	}
    	int l=1,r=n,ans=0;
    	while(l<=r) {
    		int mid=(l+r)>>1;
    		if(check(f[mid])) ans=f[mid],r=mid-1;
    		else l=mid+1;
    	}
    	ans?printf("%lld
    ",ans):printf("AFK
    ");
    	return 0;
    }
    
    

    进击的奶牛

    最小值最大化

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=100005;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    inline void Max(int &x,int y){if(x<y)x=y;}
    inline void Min(int &x,int y){if(x>y)x=y;}
    int n,c,a[N];
    bool check(int mid) {
    	int pre=a[1],cnt=1;
    	for(int i=2;i<=n;i++) {
    		if(a[i]-pre>=mid) {
    			if(++cnt==c) return 1;
    			pre=a[i];
    		}
    	}
    	return 0;
    }
    int main() {
    	n=read();c=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	sort(a+1,a+1+n);
    	int l=0,r=a[n]-a[1];
    	int ans=0;
    	while(l<=r) {
    		int mid=(l+r)>>1;
    		if(check(mid)) l=mid+1,ans=mid;
    		else r=mid-1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    实数二分

    // 1
    while(l+eps<r) {
    	double mid=(l+r)/2;
    	if(check(mid)) r=mid;
    	else l=mid;
    }
    // 2 当eps不确定时用for更精确
    for(int i=0;i<100;i++) {
    	double mid=(l+r)/2;
    	if(check(mid)) r=mid;
    	else l=mid;	
    }
    

    切绳子

    舍掉后几位:

    方法1:floor(lb*100)/100

    方法2:先所有的a[i] *100 最后答案除以100

    #include <cstdio>     
    #include <algorithm>     
    #include <iostream>    
    #include <cmath>   
    using namespace std;
    int m,n;
    double a[10005];
    bool C(double x) {
    	int num=0;
    	for (int i=1;i<=n;i++)  
    	 num+=(int)(a[i]/x);
    	return num>=m;
    }
    int main() {
    //	freopen("1.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)  
    	  	scanf("%lf",&a[i]);
    	double lb=0,ub=0x7fffffff;
    	for (int i=1;i<=100;i++) {
    		double mid=(lb+ub)/2;
    		if(C(mid)) lb=mid; else ub=mid;
    	}
    	printf("%.2f
    ",floor(lb*100)/100);
    	return 0;
    }
    

    寻找段落

    二分+单调队列好题

    二分最大平均值。

    一开始我是暴力O(N^2) 判断,只有30分;

    正解:

    我们将a全部减去mid,问题转化为判断是否存在一个长度在s~t范围内的区间它的和为正,如果有说明还有更大的平均值。

    用前缀和和单调队列维护。

    然后用单调队列求出sum[i]-min(sum[i-t]~sum[i-s]),然后判断是否大于0即可。

    #include <cmath>
    #include <cstdio>
    #include <iostream>
    
    using namespace std;
    const int N=100005;
    const int eps=1e-4;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,L,R,q[N];
    double a[N],sum[N];
    bool check(double mid) {
        for(int i=1;i<=n;i++)
        	sum[i]=sum[i-1]+a[i]-mid;	
        int h=1,t=0;
        for(int i=L;i<=n;i++) {
        	while(h<=t&&sum[q[t]]>sum[i-L]) t--;
        	q[++t]=i-L;
        	while(h<=t&&q[h]<i-R) h++;
        	if(h<=t&&sum[i]-sum[q[h]]>=0) return 1;
    	}
    	return 0;
    }
    int main() {
    	n=read();L=read();R=read();
    	for(int i=1;i<=n;i++) 
    		a[i]=read();
    	double l=0,r=10000,mid;
    	for(int i=0;i<100;i++) {
    		mid=(l+r)/2;
    		if(check(mid)) l=mid;
    		else r=mid;
    	}
    	printf("%.3lf
    ",l);
    	return 0;
    }
    
    
    

    wait:

    https://www.luogu.com.cn/problem/P1493

    聪明的质监员

    https://www.luogu.com.cn/problem/P1314

  • 相关阅读:
    【VS开发】如何判断客户端SOCKET已经断开连接?
    【VS开发】如何判断客户端SOCKET已经断开连接?
    【VS开发】从sockaddr中取得客户端或者数据源的Ip地址和端口号
    【VS开发】从sockaddr中取得客户端或者数据源的Ip地址和端口号
    【VS开发】IP地址格式转换(htonl、ntohl;inet_addr、inet_ntoa)
    【VS开发】IP地址格式转换(htonl、ntohl;inet_addr、inet_ntoa)
    【VS开发】最小化到托盘 shell_notifyicon和NOTIFYICONDATA
    【VS开发】最小化到托盘 shell_notifyicon和NOTIFYICONDATA
    【网络开发】winsock组播
    【网络开发】winsock组播
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13604753.html
Copyright © 2011-2022 走看看