二分专题(不定期更新)

1、POJ 3258 River Hopscotch

  题意:给出小溪的长度L,给出河中石头数N和最多可移动的石头数M,使得每两颗相邻石头、起点与第一颗石头、终点与最后一颗石头之间的最小距离最大。

  思路:二分最小距离,然后把间距小于该距离的石头移除,如果移除的石头数大于M,说明该距离太大,否则可以尝试增大该距离。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int L, N, M;
 5 int st, ed;
 6 const int maxn = 50005;
 7 int Rocks[maxn];
 8 bool Judge(int dis)
 9 {
10     int rmvrocks = 0;
11     int tdis = 0;
12     int refpos = 0;
13     int tnumrocks = 0;
14     for (int i = 0; i < N;)
15     {
16         tnumrocks = 0;
17         while (i<N&&Rocks[i] - refpos < dis) tnumrocks++,i++;
18         rmvrocks += tnumrocks;
19         refpos = Rocks[i];
20         i++;
21     }
22     if (tnumrocks == 0 && L - Rocks[N - 1] < dis) rmvrocks++;
23     if (rmvrocks > M) return false;
24     else return true;
25 }
26 int main()
27 {
28     while (cin >> L)
29     {
30         cin >> N >> M;
31         for (int i = 0; i < N; i++) cin >> Rocks[i];
32         sort(Rocks, Rocks + N);
33         int mindis = Rocks[0];
34         for (int i = 1; i < N; i++)
35             if (Rocks[i] - Rocks[i - 1] < mindis) mindis = Rocks[i] - Rocks[i - 1];
36         if (L - Rocks[N - 1] < mindis) mindis = L - Rocks[N - 1];
37         int l = mindis, r = L;
38         while (l <= r)
39         {
40             int mid = (l + r) / 2;
41             if (Judge(mid)) l = mid + 1;
42             else r = mid - 1;
43         }
44         cout << r << endl;
45     }
46     return 0;
47 }
View Code

2、POJ 3104 Drying

  题意:给出n件衣服,每件衣服上都有wi的水分,每次要么把一件衣服放在烘炉上,每分钟至多减少k的水分,要么自然烘干,每分钟减少1的水分,同一时间最多只能把一件衣服放在烘炉上。求所有衣服都烘干的最小时间。

  思路:二分总时间,记为为t,设此时所需用烘炉的时间为tx=0.为使总时间最小,那么要尽可能利用烘炉,直至所有衣服被烘干之前的每分钟都有把某一件衣服放在烘炉上,我们则统计出该时间,如果小于等于t,说明我们还可以尝试减少时间,否则,只能增大时间。接下来要统计出tx.第i件衣服的水分为wi.如果wi<t,则让其自然烘干即可;否则,由于只需要ceil((wi-x)/(k-1))的用烘炉的时间,剩下的水分让其自然烘干。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cmath>
 4 #include <cstdio>
 5 using namespace std;
 6 #define MAXN 100000+10
 7 #define LL long long
 8 int n, k;
 9 int wat[MAXN];
10 
11 bool Judge(int x)
12 {
13     LL t = 0;
14     for (int i = 0; i<n; i++)
15     {
16         if (wat[i]>x)
17         {
18             int xx = ceil((wat[i] - x)*1.0 / (k - 1));
19             t += xx;
20         }
21         if (t>x)return false;
22     }
23 
24     if (t>x)return false;
25     else return true;
26 }
27 int main()
28 {
29     while (~scanf("%d", &n))
30     {
31         int l = 1, r = 0;
32         for (int i = 0; i<n; i++)
33         {
34             scanf("%d", &wat[i]);
35             r = max(r, wat[i]);
36         }
37         scanf("%d", &k);
38 
39         if (k == 1)
40         {
41             printf("%d
", r);
42             continue;
43         }
44         int mid = 0;
45         while (l <= r)
46         {
47             mid = (l + r) / 2;
48             if (Judge(mid))
49             {
50                 r = mid - 1;
51             }
52             else
53                 l = mid + 1;
54         }
55         printf("%d
",l);
56     }
57     return 0;
58 }
View Code

 3、poj2976-Dropping tests(0-1分数规划)

  题意:有n次测试,每次测试对ai道题,总共bi道题。现在,你可以在最多除去k次的测试,使得剩下的测试中总正确率(剩下测试的对的总题数/剩下测试的总题数)最高。

  思路:二分总正确率,对于当前假设正确率x,对于每个测试,其额外做对的题目为ai-x*bi(也可以看成贡献度),然后去掉最小的k次,累加前面大的n-k次,如果算出来正确率可以超过x,则可以尝试增大x,否则,只能减小x.

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 1010;
 5 int n, k;
 6 struct Score
 7 {
 8     int a;
 9     int b;
10 }tests[maxn];
11 long long suma, sumb;
12 
13 double tt;//假设的当前正确率
14 bool Cmp(const Score&a, const Score&b)
15 {
16     return a.a - tt*a.b > b.a - tt*b.b;//对当前正确率贡献度大的排前面
17 }
18 
19 bool Judge(double rt)
20 {
21     tt = rt;
22     sort(tests, tests + n, Cmp);
23     long long sum1 = 0, sum2 = 0;
24     for (int i = 0; i < n; i++)
25     {
26         if(i<n-k)sum1 += tests[i].a, sum2 += tests[i].b;
27         else if (tests[i].a - rt*tests[i].b > 1e-4) sum1 += tests[i].a, sum2 += tests[i].b;
28     }
29     if (1.0*sum1 / sum2 >= rt) return true;
30     else return false;
31 }
32 
33 int main()
34 {
35     while (cin >> n >> k, n != 0 || k != 0)
36     {
37         suma = sumb = 0;
38         for (int i = 0; i < n; i++)
39         {
40             cin >> tests[i].a;
41             suma += tests[i].a;
42         }
43         for (int i = 0; i < n; i++)
44         {
45             cin >> tests[i].b;
46             sumb += tests[i].b;
47         }
48         double L = 1.0*suma / sumb;
49         double R = 1.0;
50         while (R - L > 1e-4)
51         {
52             double mid = (L + R) / 2;
53             if (Judge(mid)) L = mid;
54             else R = mid;
55         }
56         printf("%.lf
", L*100);
57     }
58     return 0;
59 }
View Code

4、poj 3685 Matrix

  题意:对于n*n的矩阵,第i行第j列的数字为i*i + 100000 * i + j*j - 100000 * j + i*j。求第m小的元素值。

  思路:从式子可以看出每列中的数字按行递增。然后二分答案,然后对于每列二分,找到第一个大于等于该答案的位置,然后就可以知道该列中小于该答案的数字的个数。统计出所有的个数,如果个数大于m,说明该答案还可以减小,否则的话需要增大。

 1 //先对答案进行二分,求满足小于等于该答案的个数(求法:由于每列从上到下的值递增,对每一列找到第一个大于答案的值,然后累加个数),若个数太多,则说明答案太大,否则太小。
 2 #include<iostream>
 3 #include<cstdio>
 4 using namespace std;
 5 const long long INF = 1LL << 50;
 6 long long f(long long i, long long j)
 7 {
 8     return i*i + 100000 * i + j*j - 100000 * j + i*j;
 9 }
10 long long n, m;
11 bool Judge(long long tans)
12 {
13     long long num = 0;
14     for (int j = 1; j <= n; j++)
15     {
16         int l = 1, r = n;
17         while (l <= r)
18         {
19             int mid = (l + r) / 2;
20             if (f(mid, j) > tans)
21             {
22                 r = mid - 1;
23             }
24             else l = mid + 1;
25         }
26         num += l - 1;
27     }
28     if (num >= m) return true;
29     else return false;
30 }
31 int main()
32 {
33     int t;
34     cin >> t;
35     while (t--)
36     {
37         scanf("%lld%lld", &n, &m);
38         long long R = INF, L = -INF;
39         while (L <= R)
40         {
41             long long mid = (L + R) >> 1;
42             if (Judge(mid))
43             {
44                 R = mid - 1;
45             }
46             else L = mid + 1;
47         }
48         printf("%lld
", L);
49     }
50     return 0;
51 }
View Code

5、poj 3662 Telephone Linse

  题意:有N个电线杆,给出P条可以连接的电线,其中K条可以免费,求存在1到N的一条路径,若连接的电线数目小于K条,则为0;若大于,则求其除去K条电线后剩余的电线长度最大值的最小值;若不存在路径,为-1.

  思路:最优情况下免费的K条肯定取需要连接的电线从大到小前K条。那么我们二分这个最大长度mid,把大于该长度的电线的权值记为1,小于等于该长度的电线的权值记为0,求一次从1开始的最短路径,dis[N]即为大于mid长度的电线条数,如果该条数小于等于K,说明我们至多需要支付mid的价格,接下来看能不能再缩小mid;否则,只能增大mid;如果不存在路径,直接输出-1;如果mid为0时dis[N]小于等于K,则可直接输出0.

  1 #include<iostream>
  2 #include<memory.h>
  3 #include<vector>
  4 #include<queue>
  5 using namespace std;
  6 struct node
  7 {
  8     int to;
  9     int length;
 10     int weight;
 11     node(int t= 0, int l = 0,int wt=0):to(t),length(l),weight(wt){ }
 12 };
 13 vector<vector<node> >m;
 14 int dis[1005];
 15 int path[1005];
 16 bool vis[1005];
 17 int cnt[1005];
 18 int n, p, k;
 19 const int INF = 0x7fffffff;
 20 bool flag = true;
 21 bool SPFA(int src)
 22 {
 23     memset(vis, 0, sizeof(vis));
 24     memset(cnt, 0, sizeof(cnt));
 25     for (int i = 1; i <= n; i++)
 26     {
 27         dis[i] = INF;
 28         path[i] = src;
 29     }
 30     queue<int>q;
 31     q.push(src);
 32     dis[src] = 0, vis[src] = true;
 33     cnt[src]++;
 34     while (!q.empty())
 35     {
 36         int v = q.front();
 37         q.pop();
 38         vis[v] = false;
 39         int sz = m[v].size();
 40         for (int i = 0; i < sz; i++)
 41         {
 42             int u = m[v][i].to;
 43             if (dis[v] + m[v][i].weight < dis[u])
 44             {
 45                 dis[u] = dis[v] + m[v][i].weight;
 46                 path[u] = v;
 47                 if (!vis[u])
 48                 {
 49                     q.push(u);
 50                     vis[u] = true;
 51                     cnt[u]++;
 52                     if (cnt[u] > n) return false;
 53                 }
 54             }
 55         }
 56     }
 57     return true;
 58 }
 59 void Dij(int src)
 60 {
 61     memset(dis, -1, sizeof(dis));
 62     memset(vis, 0, sizeof(vis));
 63     memset(path, 0, sizeof(path));
 64     int sz = m[src].size();
 65     for (int i = 0; i < sz; i++) dis[m[src][i].to] = m[src][i].weight,path[m[src][i].to]=src;
 66     for (int i = 1; i <= n; i++) if (dis[i] == -1) dis[i] = INF;
 67     vis[src] = true, dis[src] = 0;
 68     for (int i = 1; i <= n - 1; i++)
 69     {
 70         int u = src, min = INF;
 71         for (int i = 1; i <= n; i++)
 72         {
 73             if (!vis[i] && dis[i] < min) u = i, min = dis[i];
 74         }
 75         vis[u] = true;
 76         sz = m[u].size();
 77         for (int i = 0; i < sz; i++)
 78         {
 79             int v = m[u][i].to;
 80             if (!vis[v] && dis[u] + m[u][i].weight < dis[v])
 81             {
 82                 dis[v] = dis[u] + m[u][i].weight;
 83                 path[v] = u;
 84             }
 85         }
 86     }
 87 }
 88 bool Judge(int mid)
 89 {
 90     for (int i = 1; i <= n; i++)
 91     {
 92         int sz = m[i].size();
 93         for (int j = 0; j < sz; j++)
 94         {
 95             if (m[i][j].length > mid) m[i][j].weight = 1;
 96             else m[i][j].weight = 0;
 97         }
 98     }
 99     //SPFA(1);
100     Dij(1);
101     if (dis[n] == INF) flag = false;
102     if (dis[n] <= k) return true;
103     else return false;
104 }
105 int main()
106 {
107     while (~scanf("%d%d%d", &n, &p, &k))
108     {
109         m.clear();
110         for (int i = 0; i <= n; i++) m.push_back(vector<node>());
111         int L = 0, R = 0;
112         for (int i = 1; i <= p; i++)
113         {
114             int v, u, l;
115             scanf("%d%d%d", &v, &u, &l);
116             m[v].push_back(node(u, l));
117             m[u].push_back(node(v, l));
118             if (l > R) R = l;
119         }
120         flag = true;
121         if (Judge(0))
122         {
123             printf("0
");
124         }
125         else if (!flag)
126         {
127             printf("-1
");
128         }
129         else
130         {
131             while (L <= R)
132             {
133                 int mid = (L + R) / 2;
134                 if (Judge(mid)) R = mid - 1;
135                 else L = mid + 1;
136             }
137             printf("%d
", L);
138         }
139     }
140     return 0;
141 }
View Code

6、UVA 1555 - Garland(推公式,贪心,二分搜索)

  题意:有一个电线,给出最左边的电灯挂的高度,第i盏灯的高度为Hi = (H i-1 + H i+1)/2 - 1,共要挂n盏灯,并且高度不能低于地面。求最右边那盏灯的最低高度。

  思路:Hi = (H i-1 + H i+1)/2 - 1转换为Hi=2*H(i-1)+2-H(i-2)。二分第二盏灯的高度,如果没有灯的高度低于地面,则看看能不能再降低,否则只能增大。

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n;
 5 double A, B;
 6 bool Judge(double x)
 7 {//Hi=2*H(i-1)+2-H(i-2)
 8     double h1 = A, h2 = x,h3;
 9     for (int i = 3; i <= n; i++)
10     {
11         h3 = 2 * h2 + 2 - h1;
12         if (h3 < 0) return false;
13         h1 = h2, h2 = h3;
14     }
15     B = h3;
16     return true;
17 }
18 int main()
19 {
20     while (~scanf("%d%lf", &n, &A))
21     {
22         double L = 0, R = A;
23         while (R - L >= 1e-6)
24         {
25             double mid = (R + L) / 2;
26             if (Judge(mid)) R = mid;
27             else L = mid;
28         }
29         printf("%.2lf
", B);
30     }
31     return 0;
32 }
View Code
原文地址:https://www.cnblogs.com/ivan-count/p/7228957.html