ACM-二分-三分查詢筆記
思想: 分治。
適用範圍:二分只適用於單調函式,對單調遞增或單調遞減的一個序列中的某一個元素進行查詢;三分用於凸函式和凹函式。
複雜度分析:二分的時間複雜度為log2(n),而三分的時間複雜度為3log3(n)。
<<挑戰程式設計競賽>>
3.1.2 假定一個解並判斷是否可行。
Poj1064 - Cable master
題意:給出n條繩子,長度分別為Li,裁剪出m條等長且儘量長的線段,並且讓這些線段儘可能長。
#include<cstdio>
#include<math.h>
using namespace std;
#define N 11000
#define INF 1000000
double a[N];
int n, k;
double C(double x)
{
int num = 0;
for (int i = 0; i < n; i++) num += (int) (a[i] / x);
return num >= k;
}
void solve()
{
double l = 0;
double r = INF;
for (int i = 1; i < 100; i++){
double mid = (l+r)*1.0 / 2;
if (C(mid)) l = mid;
else r = mid;
}
printf("%.2f\n",floor(l*100)/100);
}
int main ()
{
//yyy_3y
freopen("1.in","r",stdin);
scanf("%d %d",&n,&k);
for (int i = 0; i < n; i++) scanf("%lf",&a[i]);
solve();
}
3.1.3、 二分最大化最小值
Poj2456 - Aggressive cow
題意:有n個牛舍,第i個牛舍在xi的位置上面,但是m頭牛會互相攻擊,因此要使得最近的兩頭牛之間的距離儘量大,求這個距離。
思路: 1:對牛舍的位置x進行排序
2:把第一頭牛放入X0 的牛舍
3.如果第i頭牛放入了xj的話,第i+1頭牛就要放入滿足
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
#define N 110000
#define INF 1000000010
int a[N];
int n, c;
bool C(int d)
{
int last = 0;
for (int i = 1; i < c; i++){
int crt = last + 1;
while (crt < n && a[crt] - a[last] < d) crt++;
// printf("i = %d ,cnt = %d\n",i,crt);
if (crt == n) return false;
last = crt;
}
return true;
}
void solve()
{
sort(a,a+n);
int l = 0;
int r = INF;
while(r - l > 1){
int mid = (l+r) / 2;
if (C(mid)) l = mid;
else r = mid;
}
printf("%d\n",l);
}
int main ()
{
//yyy_3y
// freopen("1.in","r",stdin);
scanf("%d %d",&n,&c);
for (int i = 0; i < n; i++) scanf("%d",&a[i]);
solve();
return 0;
}
補充
3.1.4 最大化平均值
Poj2976 - Dropping tests
題意:一共有N場考試,每場一共有b題,每場對a題,我們可以去掉k場的成績,使得最後的正確率最大。
思路:二分正確率。
#include<cstdio>
#include<math.h>
#include <algorithm>
#include <cstring>
using namespace std;
#define eps 1e-6
const int N = 1010;
int n,k;
double x;
struct node{
int a,b;
bool operator <(const node & c) const{
return a-x*b < c.a-x*c.b;
}
}T[N];
bool C(double mid)
{
x=mid;
sort(T+1,T+1+n);
double a=0.0,b=0.0;
for(int i=k+1;i<=n;i++){
a+=T[i].a;
b+=T[i].b;
}
return 1.0*a/b>mid;
}
void solve()
{
double l=0.0,r=1.0;
while(r-l>=eps){
double mid=(l+r)/2;
if(C(mid)) l=mid;
else r=mid;
}
printf("%.0f\n", 100.0*l);
}
int main ()
{
//yyy_3y
// freopen("1.in","r",stdin);
while(scanf("%d%d",&n,&k)&&n+k){
for (int i = 1; i <= n; i++) scanf("%d",&T[i].a);
for (int i = 1; i <= n; i++) scanf("%d",&T[i].b);
solve();
}
}
三分
概念:在二分查詢的基礎上,在右區間(或左區間)再進行一次二分,這樣的查詢演算法稱為三分查詢,也就是三分法。主要來確定最值!
1.三分半徑
Poj3737 – UmBasketella
題意:給出一個圓錐的表面積, 求該圓錐的最大體積,以及此時的底面半徑和高。
思路:數學公式推導,找到半徑的上界和下屆。三分半徑即可。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include<cmath>
#define PI acos(-1.0)
#define eps 1e-6
using namespace std;
double s;
double C(double r)
{
double R=(s/PI/r-r);
double h=sqrt(R*R-r*r);
return PI*h*r*r/3;
}
double solve(double left, double right)
{
double midl, midr;
while (right-left > eps)
{
midl = (2.0*left + right) / 3;
midr = (left + 2.0*right) / 3;
if(C(midl) >= C(midr)) right = midr;
else left = midl;
}
return left;
}
int main()
{
while(scanf("%lf",&s)!=EOF){
double left=0.0,right=sqrt(s/2/PI);
left=solve(left,right);
double R=(s/PI/left-left);
double h=sqrt(R*R-left*left);
double v=PI*h*left*left/3;
printf("%.2f\n%.2f\n%.2f\n", v, h, left);
}
return 0;
}
2.三分再三分!
hdu 3400 – Line belt
題意:有兩條傳送帶,傳送帶上有兩段,AB和CD,它們的速度分別是P和Q,其它地方的速度為R,問從A到D所需要的最短時間是多少。
思路:先三分AB上的點,再三分CD上的點即可。
設X在AB上,Y在CD上。
人線上段AB上花的時間為:f(x) = AX / P,
人走完Z和Y所花的時間為:g(x) = XY / R + YD / Q。
f(x)+g(x)很明顯是一個先遞減後遞增的函式。故可用三分法。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include<cmath>
#define PI acos(-1.0)
#define eps 1e-6
using namespace std;
struct node{
double x,y;
}a,b,c,d,X,Y;
double p,q,r;
double dis(node a,node b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double calc(double mid)
{
Y.x=c.x+(d.x-c.x)*mid;
Y.y=c.y+(d.y-c.y)*mid;
return dis(Y,d)/q + dis(X,Y)/r;
}
double C(double mid)
{
X.x=a.x+(b.x-a.x)*mid;
X.y=a.y+(b.y-a.y)*mid;
double l=0.0,r=1.0,midl,midr;
double ans;
while(r-l>eps){
midl=(2.0*l+r)/3;
midr=(l+2.0*r)/3;
ans=calc(midl);
if(ans<=calc(midr)) r=midr;
else l=midl;
}
return dis(a,X)/p+ans;
}
void solve()
{
double l=0.0,r=1.0,midl,midr;
double ans;
while(r-l>eps){
midl=(2.0*l+r)/3;
midr=(l+2.0*r)/3;
ans=C(midl);
if(ans<=C(midr)) r=midr;
else l=midl;
}
printf("%.2f\n",C(l));
}
int main ()
{
int t; scanf("%d",&t);
while(t--){
scanf("%lf%lf%lf%lf",&a.x,&a.y,&b.x,&b.y);
scanf("%lf%lf%lf%lf",&c.x,&c.y,&d.x,&d.y);
scanf("%lf%lf%lf",&p,&q,&r);
solve();
}
}
3. 三分整數型別(平均值)
codeforces — 939E. Maximize!
想到總結三分也是應為這一道題。這道題可以拿尺取法解—->尺取法
但是三分也是一種非常優秀的思路。
題意:
有兩種操作。
1.往集合裡面加一個數(這個數為集合中最大的數)。
2.在該集合取任一子集使得Max子集(子集中最大的元素)-AVG子集 最大
#include<bits/stdc++.h>
#define debug(a) cout << #a << " " << a << endl
#define LL long long
#define PI acos(-1.0)
#define eps 1e-6
const int N=1e6+7;
using namespace std;
LL sum[N],x;
int cnt=0;
double ans=0;
double C(int mid)
{
return 1.0*(sum[mid]+x)/(mid+1);
}
double solve(int l,int r)
{
int midl,midr;
while(r-l>2){
midl=(2.0*l+r)/3;
midr=(l+2.0*r)/3;
if(C(midl)<=C(midr)) r=midr;
else l=midl;
}
return min(min(C(l),C(r)),C((l+r)/2));
}
int main ()
{
//yyy_3y
//freopen("1.in","r",stdin);
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
int type; scanf("%d",&type);
if(type==1){
scanf("%lld",&x);
sum[++cnt]=sum[cnt-1]+x;
}
else {
double tmp=solve(1,cnt-1);
ans=x-tmp;
printf("%.10f\n",ans);
}
}
return 0;
}
相關文章
- 二分查詢法小記
- 查詢——二分查詢
- 二分查詢(一)——純粹的二分查詢
- Python查詢-二分查詢Python
- 二分查詢
- 查詢演算法__二分查詢演算法
- 順序查詢和二分查詢
- PHP二分查詢PHP
- 二分查詢法
- 查詢演算法之二分查詢演算法
- 二分查詢 | 二分查詢的一種推薦寫法
- acm-排列組合學習筆記(更新中)ACM筆記
- 【資料結構】折半查詢(二分查詢)資料結構
- 二分插入與二分查詢
- 二分查詢(c++)C++
- BinarySearch(二分查詢)
- 704.二分查詢
- 詳解二分查詢
- MYSQL學習筆記25: 多表查詢(子查詢)[標量子查詢,列子查詢]MySql筆記
- MYSQL學習筆記26: 多表查詢|子查詢MySql筆記
- 二分查詢【折半查詢】演算法 PHP 版演算法PHP
- leetcode -- 二分查詢LeetCode
- PHP 實現二分查詢PHP
- 【演算法】二分查詢演算法
- python二分查詢模板Python
- 二分查詢成長錄
- 圖解--二分查詢樹圖解
- 二分搜尋(折半查詢)
- 二分查詢的定義
- leetcode——二分查詢LeetCode
- 力扣之二分查詢力扣
- 二分查詢-不套用模板
- 二分查詢演算法演算法
- 演算法->二分查詢演算法
- 牛客網 查詢(二分查詢、北郵機試)
- 子查詢學習筆記1筆記
- 二分查詢基礎專題——二分模板
- 模板 - 二分&三分