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 }
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 }
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 }
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 }
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 }
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 }