A - Funky Numbers
题意:给一个数num(max = 1e9),询问这个数可否由两个triangular number组成,triangular number:k(k + 1) / 2。
思路:最简单的思路就是打表,因为这个数虽然是1e9,但是可以从1开始打表到1e6(注意必须是1e6,因为1e5 * 9999 < 1e9),任意两个相邻的数的乘积,然后枚举第一个数,二分查找第二个数,最关键的是(two triangular numbers (not necessarily different !),那我也不用谈判是否重复了,直接使用二分函数binary_search()即可。
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 1e6;
typedef long long ll;
using namespace std;
ll a[N];
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
for( ll i = 0; i < N; i++ ){
a[i] = ( i + 1 ) * ( i + 2 ) / 2;
}
ll num;
cin >> num;
int f = 1;
for( ll i = 0; i < N; i++ ){
if( binary_search( a, a + N, num - a[i] ) ){
cout << "YES";
f = 0;
break;//找到直接退出即可
}
}
if( f ){
cout << "NO";
}
return 0;
}
B - 银行贷款
思路:写出公式就可以了,注意分母下面是n方。然后 L = 0, R = 1000,二分查找就可以了,注意的是如果check( mid ) >= x,说明月利率小了。还有这道题我用的是pow( ),其实最好是不要用这个函数,容易出现精度问题。这道题可以作为浮点数二分的板子。
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
typedef long long ll;
using namespace std;
int x, y, n;
double check( double mid ){
double sum = 0;
for( int i = 1; i <= n; i++ ){
sum += y / ( pow( 1 + mid / 100.0 , i ) );
}
return sum;
}
double b_search( ){
double l = 0, r = 1000;
int cnt = 100;
while( cnt-- ){
double mid = ( r + l ) / 2;
// cout << " l : " << l << " r : " << r << " mid : " << mid << endl;
if( check( mid ) >= x )
l = mid;//浮点数不要随意的加减1,要出问题直接这样就可
else
r = mid;
}
return l;
}
int main()
{
scanf("%d%d%d", &x, &y, &n );
double ans = b_search();
printf("%.1f", ans );//注意题意的精度问题
return 0;
}
C - River Hopscotch
题意:
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
思路:这是最大最小问题,一般这种问题我们都是考虑二分求解,但也不全是,要具体情况具体分析。最大是通过b_search( )函数来实现,最小就是两两之间距离最小。这道题为了解题方便,把起点和终点的岩石也加入数组中。注意这道题没有说给出的岩石位置顺序就是从小到大的,所以需要sort排序,(而且有些题要注意方向,有些说的是与起点的距离,有些说的是与终点的距离。),而且这道题最小的 L == 原本的最短距离, R == 整个路的长度,然后进行二分。如何check成为关键点,我最开始是计算有多少个不满足的点,其实这样算是不对的,因为你一旦移开了一个石头,可能2个大于的点变成了一个,所以正确的解法是模拟人的真实进行过程,不断更新现在位置如果a[i] - nw < mid,就不满足,要满足就要搬石头,注意细想一下为什么是小于,而不是大于等于,如果满足这个mid就是针对这段距离的最小距离,就很好直接更新当前位置,直接跳过去。如果cnt小于等于m,说明这个长度很合适,而且cnt越小于m,len越合适,注意不一定要等于,因为小于了但满足条件,我可以多搬几个除了最小长度以外的石头。
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 5e4 + 10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
using namespace std;
ll l, n, m, a[N], minn = INF;
bool check( ll mid ){
ll cnt = 0;
ll nw = 0;
for( int i = 1; i < n + 2; i++ ){
if( a[i] - nw < mid ){
++cnt;
}
else{
nw = a[i];
}
}//妙啊
return ( cnt <= m );
}
ll b_search( ){
ll L = minn, R = l;//注意尽量剪少时间复杂度
while( L < R ){
ll mid = L + ( ( R - L ) >> 1 ) + 1;
if( check( mid ) )
L = mid;
else
R = mid - 1;
}
return L;
}
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
cin >> l >> n >> m;
a[0] = 0;
for( int i = 1; i <= n; i++ ){
cin >> a[i];
}
a[n + 1] = l;
sort( a, a + n + 2 );
for( int i = 1; i < n + 2; i++ ){
minn = min( minn, a[i] - a[i - 1] );
}
cout << b_search();
return 0;
}
D - Pie
题意:
我的生日快到了,并且我习惯上(traditionally)准备派。不仅仅是一个派,我有N个各种(various)味道各种大小的派。F个朋友要参加我的派对,并且每人都会得到一份派。他们每人都应该拿到一块派,并不是那种好几小片的看起来乱糟糟的。这应该是一整块派。如果其他人分到了较大的一块派,他们会生气并开始抱怨。所以他们都应该得到一块相同大小但不必是相同口味的派,虽然会导致一些派被糟蹋(spoil),也好过糟蹋派对。当然,我也想要一块派,并且这一块也得和其他人一样大。最大的问题是我们每人能得到多大一块派?所有派都是圆柱形的(cylindrical),并且它们都有相同的高——1,但是这些派的半径不同。也就是说不能合并pie,只能一个从一块中取。
思路:很明显这个是二分体积同时是个浮点数二分。L = 0, R = 最大的半径,同时这道题为了方便,把pi都先不写,把体积用r^2表示,最后再乘上pi,check()函数就是看最后的数量与要求的数量的关系,如果大于了,说明分的半径小了。这道题很恶心的一点是居然卡我cin,cout,还必须用C++,#define pi acos( -1.0 )只能这样定义。(读懂题很重要)
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <cmath>
const int N = 1e4 + 10;
#define pi acos( -1.0 );
using namespace std;
int t, n, f;
double a[N], maxx;
bool check( double mid ){
int cnt = 0;
for( int i = 0; i < n; i++ ){
cnt += ( int )( a[i] / mid );
}
return ( cnt >= f );
}
double b_search( ){
double l = 0, r = maxx;
int cnt = 100;
while( cnt-- ){
double mid = ( l + r ) / 2;
if( check( mid ) )
l = mid;
else
r = mid;
}
return l;
}
int main()
{
scanf("%d", &t );
while( t-- ){
scanf("%d%d", &n, &f);
maxx = -1;
++f;//细节,他自己还要吃蛋糕
for( int i = 0; i < n; i++ ){
scanf("%lf", &a[i] );
a[i] *= a[i];//方便运算
maxx = max( maxx, a[i] );//减少时间复杂度
}
double ans = b_search() * pi;
printf("%.4lf\n", ans );
}
return 0;
}
F - K Best
题意:有n个物品的重量和价值分别是w[i]和v[i],从中选出K个物品使得单位重量的价值最大。
思路:最开始这道题的思路就是贪心,把aver权值算出来,然后取最大的前k个,但是后面发现是不对的。如果按照价值/体积排序,贪心选取之后并不能保证最后的总平均价值最大。转化为不等式就是a+w1/b+v1>a+w2/b+v2,w1/v1>w2/v2 并不能保证上述不等式成立,每一种策略对式子的影响很大,不是简单的贪心,但是复杂的贪心我也不会啊~,走上正途,最大化均值问题用二分,而且这道题很明显不是整数二分,而是浮点数二分。啊啊啊!不想细写了,直接上代码~
#include <stdio.h>
#include <cstring>
#include <algorithm>
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
using namespace std;
struct node{
double aver;
int loc;
}a[N];
int n, k, ans[N];
double v[N], w[N];
bool cmp( node a, node b ){
return a.aver > b.aver;
}
bool check( double mid ){
for( int i = 0; i < n; i++ ){
a[i].aver = v[i] - mid * w[i];
a[i].loc = i;
}
double sum = 0;
sort( a, a + n, cmp );
for( int i = 0; i < k; i++ ){
sum += a[i].aver;
ans[i] = a[i].loc;
}
return ( sum >= 0 );
}
void b_search( ){
double l = 0, r = INF;
int cnt = 50;
while( cnt -- ){
double mid = ( r + l ) / 2;
if( check( mid) )
l = mid;
else
r = mid;
}
}
int main()
{
scanf("%d%d", &n, &k);
for( int i = 0; i < n; i++ ){
scanf("%lf%lf", &v[i], &w[i]);
}
b_search();
int y = 1;
for( int i = 0; i < k; i++ ){
if( y == 1 ){
printf("%d", ans[i] + 1 );
}
else{
printf(" %d", ans[i] + 1 );
}
++y;
}
return 0;
}
总结:
学习了整数二分和浮点二分,明白了两种整数二分之间的差别,以及二分的坑点。