Summary
昨天的题目预测命中率71.43%(10/14)预测里有两个重复的,又删了几个
比赛只持续了2个小时 所以 Information 的 AC/Submit 看看就好 …
难道这就是传说中防 AK 的方法吗? w(゚Д゚)w
寒假训练也渐渐接近了尾声,纪念一下我们的兴安熊族和一些神奇的Nickname吧
Information
No. | Title | AC/Submit |
---|---|---|
A | 二分查找 | 113/207 |
B | 小清新的二分查找之旅 | 41/142 |
C | 小清新的函数坐标-二分 | 21/53 |
D | 小清新的二倍问题加强版-二分-桶排 | 16/91 |
E | 简单几何-二分 | 0/2 |
F | 小清新切绳子-二分 | 4/18 |
G | 卖古董-DP-二分 | 2/6 |
H | 切绳子实数版-二分 | 5/8 |
I | 数列分段-二分 | 2/4 |
J | 二分查找加强版 | 50/90 |
Problem A: 二分查找 (956)
Tips
嘘!~ ~ ~ 我不会告诉你这道题可以边读边比较的。
我还不会告诉你的是本蒟蒻最开始输出的是大于等于x的数的下标并且还AC了
好吧 ╮(╯-╰)╭ 下面的代码已经更正
另外推荐两个好用的C++函数 upper_bound()
和 lower_bound()
这两个函数的用法详见 C/C++ 二分查找算法
Code
#include <bits/stdc++.h>
using namespace std;
int num[1000005];
int main()
{
ios::sync_with_stdio(false);
int n,x,l,r,mid;
while(cin>>n>>x)
{
for(int i=0;i<n;i++)cin>>num[i];
l=0;
r=n-1;
while(l<=r)
{
mid=(l+r)/2;
if(num[mid]<=x)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<l<<endl;
}
return 0;
}
Problem B: 小清新的二分查找之旅 (1646)
Tips
直接二分即可,注意输出内容和大小写 = ̄ω ̄=
来是come 去是 go 不存在是 YES 存在是 no ~
Code
#include <bits/stdc++.h>
using namespace std;
int num[1000000];
int main()
{
int n,q,l,r,mid,k;
while(scanf("%d %d",&n,&q)!=-1)
{
for(int i=0;i<n;i++)scanf("%d",&num[i]);
for(int i=0;i<q;i++)
{
l=0;
r=n-1;
scanf("%d",&k);
while(l<=r)
{
mid=(l+r)/2;
if(num[mid]==k)break;
if(l==r)break;
if(num[mid]<k)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
if(num[mid]==k)printf("no\n");
else printf("YES\n");
}
}
return 0;
}
Problem C: 小清新的函数坐标-二分 (1645)
Tips
还好没碰到玄学精度问题 直接二分 -20 ~ 20 即可
Code
#include <bits/stdc++.h>
using namespace std;
double f(double x)
{
return 0.0001*pow(x,5)+0.003*pow(x,3)+0.5*x-3;
}
int main()
{
double y,l,r,mid;
while(scanf("%lf",&y)!=-1)
{
l=-20;
r=20;
while(l<=r)
{
mid=(l+r)/2;
if(fabs(f(mid)-y)<1e-5)break;
if(f(mid)<y)
{
l=mid;
}
else
{
r=mid;
}
}
printf("%.4f\n",mid);
}
return 0;
}
Problem D: 小清新的二倍问题加强版-二分-桶排 (1647)
Tips
能用桶排坚决不二分 (ง •_•)ง 简单粗暴 tong
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,tong[100001],ans,tmp;
scanf("%d",&n);
while(n--)
{
ans=0;
memset(tong,0,sizeof(tong));
for(int i=0;scanf("%d",&tmp)!=-1&&tmp!=0;i++)tong[tmp]++;
for(int i=0;i<=50000;i++)ans+=tong[i]*tong[2*i];
printf("%d\n",ans);
}
return 0;
}
Problem E: 简单几何-二分 (1303)
Tips
这题就遇到了玄学的精度问题,参照题解发现了一个神奇的地方
如果这样写就会 WA
while(r-l>1e-8)
{
mid=(l+r)/2;
if(pi*mid*mid*h-mid*h<=pow(mid,pi))r=mid;
else l=mid;
}
这样写就可以 AC
while(l<r)
{
mid=(l+r)/2;
if (r-l<=1e-8) break;
if(pi*mid*mid*h-mid*h<=pow(mid,pi))r=mid;
else l=mid;
}
WA 了无数次的我输出了所有的测试数据,发现了3个玄学精度问题
每个问题都差 0.0001 ( o=^•ェ•)o ┏━┓
本蒟蒻实在想不到为啥了,等待大佬解答吧…
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,h,cmp;
double y,l,r,mid,res,pi=acos(-1);
while(scanf("%d",&n)!=-1)
{
while(n--)
{
scanf("%d",&h);
l=0;
r=100000;
while(l<r)
{
mid=(l+r)/2.0;
if (r-l<=1e-8) break;
if(pi*mid*mid*h-mid*h<=pow(mid,pi))r=mid;
else l=mid;
}
printf("%.4f\n",mid);
}
}
return 0;
}
Problem F: 小清新切绳子-二分 (1648)
Tips
二分答案法
- 左边界为把最长的绳子切成 k 段,每段绳子的长度
- 右边界为最长绳子的长度
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,k,rope[10000],l,r,mid,cnt,max,ans;
while(scanf("%d %d",&n,&k)!=-1)
{
ans=max=0;
for(int i=0;i<n;i++)
{
scanf("%d",&rope[i]);
if(rope[i]>max)max=rope[i];
}
l=max/k?max/k:1;
r=max;
while(l<=r)
{
mid=(l+r)/2;
cnt=0;
for(int i=0;i<n;i++)
{
cnt+=rope[i]/mid;
}
if(cnt>=k)
{
if(mid>ans)ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}
Problem G: 卖古董-DP-二分 (1211)
Tips
就是下面的 “Problem I: 数列-二分” 加了个多组
思路写在下面的 I 题了
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t,n,m,num[100000],l,r,mid,max,ans,min,sum,cnt;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
sum=ans=max=0;
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
if(num[i]>max)max=num[i];
sum+=num[i];
}
min=l=max;
r=sum;
while(l<=r)
{
cnt=0;
sum=0;
mid=(l+r)/2;
for(int i=0;i<n;i++)
{
if(sum+num[i]<=mid)sum+=num[i];
else
{
sum=num[i];
cnt++;
}
}
if(cnt>=m)l=mid+1;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
Problem H: 切绳子实数版-二分 (1751)
Tips
二分答案法
这道题来自洛谷的 P1577 切绳子
可以到洛谷评测(主要是为了测试数据)
要考虑的情况比较多 比如说如果你的结果是 2.456
使用 printf("%.2f",ans);
时会自动四舍五入变成 2.46 这样绳子就不够长了
所以可以把第三位小数清零来解决这个问题
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,k,cnt;
double rope[10000],max,l,r,mid,sum;
while(scanf("%d %d",&n,&k)!=-1)
{
sum=max=0;
for(int i=0;i<n;i++)
{
scanf("%lf",&rope[i]);
if(rope[i]>max)max=rope[i];
sum+=rope[i];
}
l=max/k;
r=sum/k+1e-5; //有边界稍微大一点避免二分扫不到边界值
mid=(l+r)/2;
while(r-l>=1e-5)
{
mid=(l+r)/2;
cnt=0;
for(int i=0;i<n;i++)
{
cnt+=rope[i]/mid;
}
if(cnt>=k)l=mid;
else r=mid;
}
mid-=(int)(mid*1000)%10/1000.0; //避免第三位小数四舍五入
printf("%.2f\n",mid);
}
return 0;
}
Problem I: 数列分段-二分 (1733)
Tips
欸?这不就是 G 题少个多组吗 看看 G 题就会了 ~
开个玩笑 ( •̀ ω •́ )y 这个题也是 二分答案法
- 左边界是所有数中最大的那个数
- 右边界是所有数的和
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m,num[100000],l,r,mid,max,ans,sum,cnt;
while(scanf("%d %d",&n,&m)!=-1)
{
sum=ans=max=0;
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
if(num[i]>max)max=num[i];
sum+=num[i];
}
l=max;
r=sum;
while(l<=r)
{
cnt=0;
sum=0;
mid=(l+r)/2;
for(int i=0;i<n;i++)
{
if(sum+num[i]<=mid)sum+=num[i];
else
{
sum=num[i];
cnt++;
}
}
if(cnt>=m)l=mid+1;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
Problem J: 二分查找加强版 (1245)
Tips
这题感觉和 A 题没啥区别,多了一个 sort(num,num+n);
果然是加强版,多写了整整一行代码呢 ヾ(•ω•`)o
Code
#include <bits/stdc++.h>
using namespace std;
int num[2000005];
int main()
{
ios::sync_with_stdio(false);
int n,x,l,r,mid;
while(cin>>n>>x)
{
for(int i=0;i<n;i++)
{
cin>>num[i];
}
sort(num,num+n);
l=0;
r=n-1;
while(l<=r)
{
mid=(l+r)/2;
if(num[mid]<=x)
{
l=mid+1;
}
else
{
if(mid>0&&num[mid-1]<x)break;
r=mid-1;
}
}
cout<<mid<<endl;
}
return 0;
}