二分查找 2016.3.17

本文分享了五道程序设计竞赛题目(包括POJ3061Subsequence、HDU2199Canyousolvethisequation?、UVa10706NumberSequence、HDU2289Cup、HDU1969Pie)的解题过程,涉及二分查找、尺取法、贪心算法等方法,并总结了常见错误和解决思路。

1、POJ 3061 Subsequence

Solving process from : 《挑战程序设计竞赛》

二分法:

这里写图片描述
复杂度O(nlogn)

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 100000 + 5;
int num[maxn];
int sum[maxn];

int main()
{
//    freopen("in.txt", "r", stdin);
    int T;

    while (cin>>T) {
        while (T--) {
            int N, S;
            cin>>N>>S;
            for (int i=0; i<N; ++i) {
                cin>>num[i];
            }
            sum[0] = 0;
            for (int i=0; i<N; ++i) {
                sum[i+1] = sum[i] + num[i];
            }
            if (sum[N] < S) {
                cout<<0<<endl;
            } else {
                int Min = N;
                for (int i=0; sum[i]+S<=sum[N]; ++i) {
                    int low = i, high = N;
                    while (high - low > 1) {
//                        cout<<high<<" "<<low<<endl;
                        int mid = (low+high) / 2;
                        if (sum[mid] - sum[i] >= S) {
                            high = mid;
                        } else {
                            low = mid;
                        }
                    }
                    int min_len = high - i;
//                    cout<<min_len<<endl<<endl;
                    if (min_len < Min) {
                        Min = min_len;
                    }
                }
                cout<<Min<<endl;
            }
        }
    }
    return 0;
}

尺取法:

这里写图片描述
复杂度O(n)
这里写图片描述
像这样反复地推进区间的开头和末尾,来求取满足条件的最小区间的方法被称为尺取法。

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 100000 + 5;
int num[maxn];

int main()
{
//    freopen("in.txt", "r", stdin);
    int T;

    while (cin>>T) {
        while (T--) {
            int N, S;
            cin>>N>>S;
            for (int i=0; i<N; ++i) {
                cin>>num[i];
            }
            int sum = 0;
            int Min = N + 1;
            int i = 0, j = 0;
            while (1) {
                while (i<N && sum<S) {
                    sum += num[i];
                    ++i;
                }
                if (sum < S) {
                    break;
                }
                int min_len = i - j;
                if (min_len < Min) {
                    int t = min_len;
                    min_len = Min;
                    Min = t;
                }
                sum -= num[j];
                ++j;
            }
            if (Min > N) {
                cout<<0<<endl;
            } else {
                cout<<Min<<endl;
            }
        }
    }
    return 0;
}

2、HDU 2199 Can you solve this equation?

没有判断可以计算的最大值 TLE4次

#include <iostream>
#include <cstdio>

using namespace std;

double Y;

double my_pow(double x, int n)
{
    double re = 1.0;
    while (n--) {
        re *= x;
    }
    return re;
}

const double Max = 8.0*my_pow(100, 4) + 7.0*my_pow(100, 3) + 2.0*my_pow(100, 2) + 3.0*100 + 6;

void Search(double low, double high)
{
    double mid = (low + high) / 2.0;
    double result =8.0*my_pow(mid, 4) + 7.0*my_pow(mid, 3) + 2.0*my_pow(mid, 2) + 3.0*mid + 6;
    while (fabs(result-Y) > 1e-4) {
        if (result > Y) {
            high = mid;
        } else {
            low = mid;
        }
        mid = (low + high) / 2.0;
        result =8.0*my_pow(mid, 4) + 7.0*my_pow(mid, 3) + 2.0*my_pow(mid, 2) + 3.0*mid + 6;
    }
    printf("%.4lf\n", mid);
}

int main()
{
//    freopen("in.txt", "r", stdin);
    int T;

    cin>>T;
    while (T--) {
        cin>>Y;
        if (Y < 6 || Y > Max) {
            printf("No solution!\n");
        } else {
            Search(0, 100);
        }
    }
    return 0;
}

3、UVa 10706 Number Sequence(数字序列)

刚开始理解错了题意 WA 2次
看了一眼别人的AC代码开始打表
然后用__int64 CE 2次
没有给number[0]赋值,二分找的时候,low初始赋值为1,所以在输入1的时候会出错 WA 2次
number[0]赋值0,low初始赋值0 AC

#include <iostream>
#include <cmath>
#include <cstdio>
#include <stack>

using namespace std;

const int n_maxn = 31300;
const int dig_maxn = 145400;
long long number[n_maxn];
int dig[dig_maxn];
stack<int> Stack;

void Init(void);

int main()
{
//    freopen("in.txt", "r", stdin);
    Init();
    int t;

    while (cin>>t) {
        while (t--) {
            long long i;
            cin>>i;
            int low = 0;
            int high = n_maxn - 1;
            while (high - low > 1) {
                int mid = (low+high) / 2;
                if (number[mid] >= i) {
                    high = mid;
                } else {
                    low = mid;
                }
            }
            cout<<dig[i - number[low]]<<endl;
        }
    }
    return 0;
}

void Init(void)
{
    number[0] = 0;
    number[1] = 1;
    for (int j=2; j<n_maxn; ++j) {
        int t = j;
        int Count = 0;
        while (t > 0) {
            t /= 10;
            ++Count;
        }
        number[j] = number[j-1] + Count;
    }
    for (int j=2; j<n_maxn; ++j) {
        number[j] += number[j-1];
    }
    int j = 1, k = 1;
    while (j < dig_maxn) {
        int t = k;
        while (t > 0) {
            Stack.push(t%10);
            t /= 10;
        }
        while (!Stack.empty()) {
            dig[j++] = Stack.top();
            Stack.pop();
        }
        ++k;
    }
}

4、HDU 2289 Cup

圆台体积公式:(1.0/3.0)* PI * h * (R * R+r * r+R * r)
π的宏定义

#define PI acos(-1.0)
#define PI (atan(1.0) * 4.0)

这种题一般都是要二分找答案的

然而本辣鸡在只知道圆柱和圆锥的体积计算公式的情况下开始推公式,并没有想到用二分

具体的错误做法:
(1)因为推出来的公式比较2B,所以需要判断是否圆柱(r == R),注意,其实在这里(判断两个浮点数是否相等)就已经错了,这种做法是不可行的,应该尽量避免
(2)按照错误的做法继续
如果是圆柱,那么就根据圆柱体积公式计算水的高度
否则按照圆锥体积公式推出来的公式计算
在这里我学会了求一个数的开三次方pow(x, (1.0/3.0));
(3)终于代码敲完,造了两个数据来跑,毕竟自己推出来的公式,感觉结果也不是特别离谱
(4)WA 3次

要用二分了,用了上面的两个公式来算(当时对自己的公式还比较满意,后来才发现,自己的公式实在有些SB),仍然需要判断(r == R)
(TLE + WA ) * N 次

看了别人代码,竟然还没有发现问题所在
WA * N次

终于发现问题所在。。。
AC
睡一觉再起来写博客,存代码

#include <iostream>
#include <cmath>
#include <cstdio>
#include <iomanip>

using namespace std;

#define PI (atan(1.0) * 4.0)

int main()
{
//    freopen("in.txt", "r", stdin);
    int T;
    cout.setf(ios::fixed);
    while (cin>>T) {
        while (T--) {
            double r, R, H, V;
            cin>>r>>R>>H>>V;
//                double low_H = r*H / (R-r);
            double low = 0.0, high = 100.0;
            double mid;
            while (high - low > 1e-7) {
                mid = (low + high) / 2.0;
//                    double nr = (low_H + mid) * r / low_H;
                double nr = mid / H *(R-r) + r;
                double nV = PI / 3.0 * (nr*nr + r*r + nr*r) * mid;
                if (nV > V) {
                    high = mid;
                } else {
                    low = mid;
                }
            }
            cout<<setprecision(6)<<mid<<endl;
        }
    }
    return 0;
}

5、HDU 1969 Pie

这种可恶的卡精度题都是用二分来搞
当时训练时看完题不是很懂题意,也没思路
结合别人AC代码才搞懂这道题
有贪心的思想在里面
然而 WA*N总是避免不了,QAQ(昵称要改)

题意:
每个人分到的饼是整块的且体积相等,求朋友和我分到饼的最大体积
解题思路:
从0到最大面积二分找,具体看代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <iomanip>
#include <cstdlib>

using namespace std;

#define PI acos(-1.0)

const int maxn = 10000 + 5;
double radii[maxn];

int cmp_num(const void *a, const void *b)
{
    return (*(double *)b > *(double *)a) ? 1 : -1;
}

int main()
{
//    freopen("in.txt", "r", stdin);
    int T;
    cout.setf(ios::fixed);
    while (cin>>T) {
        while (T--) {
            int N, F;
            cin>>N>>F;
            for (int i=0; i<N; ++i) {
                cin>>radii[i];
                radii[i] = PI * radii[i]*radii[i];
            }
            qsort(radii, N, sizeof(radii[0]), cmp_num);
            if (F+1 < N) {
                N = F+1;
            }
            double low = 0.0;
            double high = radii[0];
            double mid;
            while (high - low > 1e-5) {
                mid = (low + high) / 2.0;
                int Count = 0;
                for (int i=0; i<N; ++i) {
                    Count += floor(radii[i]/mid);
                }
                if (Count >= F+1) {
                    low = mid;
                } else {
                    high = mid;
                }
            }
            cout<<setprecision(4)<<low<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值