二分查找的基本思想:
将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果xa[n/2],则只要在数组a的右半部搜索x。
时间复杂度:O(log2(n))
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
例如,平时购物找零钱时,为使找回的零钱的硬币数最少,不要求找零钱的所有方案,而是从最大面值的币种开始,按递减的顺序考虑各面额,先尽量用大面值的面额,当不足大面值时才去考虑下一个较小面值,这就是贪心算法 。
题目描述
Farmer John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,…,xN (0<=xi<=1,000,000,000)。
他的C(2<=C<=N)头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?
输入格式
第1行:两个用空格隔开的数字N和C。
第2~N+1行:每行一个整数,表示每个隔间的坐标。
输出格式
输出只有一行,即相邻两头牛最大的最近距离。
AC代码:
#include
#include
using namespace std;
const int N=1e5;
int n,c;
int a[N];
bool check(int x) //判断x是否可行
{
int cnt=1; //记录可以安置牛的隔间数,a[0]一定可以放,所以cnt初始化为1
int l=0; //上一个符合条件的隔间的下标
for(int i=1;i<n;i++)
//因为已经排过序,所以我们 直接一个一个向后遍历得到的cnt一定是最大的
{
if(a[i]-a[l]>=x) //两个隔间之间距离大于x
{
cnt++;
l=i;
}
}
if(cnt>=c) return 1; //如果可以安置牛的隔间数大于牛的个数
else return 0;
}
int main()
{
cin>>n>>c;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n); //从小到大排序
int l=1,r=a[n-1]-a[0],mid,ans=-1;
//l为左界,即距离最小值,r为右界,即距离最大值。
while(l<=r)
//l=r时也需要判断,因为我们并不清楚该值是否为我们需要查找的答
{
mid=(l+r)/2; //二分查找
if(check(mid)) //如果mid符合条件
{
if(mid>ans) ans=mid; //我们要的是最大值
l=mid+1; //向右查找有没有更大的
}
else r=mid-1; //mid不符合条件,说明mid大了,向左查找
}
cout<<ans<<endl;
return 0;
}
给定一个正整数m,你需要找到一个最小的正整数n,满足n!的末尾恰好有m个零。
Input
第一行为一个整数T代表数据组数。(T<=1e4)
接下来T行,每组样例一行。
每组样例输入一个整数m。(1<=m<=1e8)
Output
对于每组样例,按照"Case i: x"或者"Case i: impossible"的格式输出。
(如果不存在任何一个数的阶乘末尾有m个零,输出impossible)
Sample Input
3
1
3
5
Sample Output
Case 1: 5
Case 2: 15
Case 3: impossible
思路:
因为m,t的数据很大,暴力求解一定会TLE,所以我们二分查找
n!的末尾恰好有m个零
==>n!恰好可以整除m次10
因为10=2*5,n!的因子中2的个数一定大于5的个数,
所以因子中有多少个5就能凑够多少个10
==>n!因子中有m个5
n/5 = n!中有多少个5的倍数
+
n/25 = (n/5)/5 = n!中有多少个25的倍数
+
.....
||
n!的因子中有多少个5
如 n=33
n/5=6 5 10 15 20 25 30
n/25=6/5=1 25
3 10 15 20 25(2) 30
cnt=6+1=7
AC代码:
#include
using namespace std;
const int N=1e9;
//x的阶乘末尾0的个数,也就是求x!可以整除多少个10
int cnt(int x) //10的因子为2和5,2的个数一定比5多
{ //所以我们只需找x!因子5的个数
int ans=0;
while(x) //比如x=44,则ans=44/5+44/25(44中为5的倍数的个数+25的倍数的个数)
{ // =44/5+(44/5)/5
ans+=x/5; // = 8+1=9
x/=5; // 5 10 15 20 25(2个5) 30 35 40
}
return ans;
}
int main()
{
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
int m;
cin>>m;
int l=1,r=N,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)<m) l=mid+1;
else
{
if(check(mid)==m)
ans=mid;
r=mid-1;
}
}
cout<<"Case "<<++i<<": ";
if(ans==-1) cout<<"impossible"<<endl;
else cout<<ans<<endl;
}
return 0;
}
Now, here is a fuction:
F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)
Can you find the minimum value when x is between 0 and 100.
Input
The first line of the input contains an integer T(1<=T<=100) which means the number of test cases. Then T lines follow, each line has only one real numbers Y.(0 < Y <1e10)
Output
Just the minimum value (accurate up to 4 decimal places),when x is between 0 and 100.
Sample Input
2
100
200
Sample Output
-74.4291
-178.8534
思路:
对F(x)求导,导函数单调递增,导函数为0的点F(x)最小。
AC代码:
#include
#include
#include
using namespace std;
double fun_1(double x,double y)
{
return 42*pow(x,6)+48*pow(x,5)+21*pow(x,2)+10*x-y;
}
double fun_2(double x,double y)
{
return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;
}
int main()
{
int t;
scanf("%d",&t);
double x,y,eps=1e-10;
while(t--)
{
scanf("%lf",&y);
double l=0,r=100,mid;
while(r-l>eps)
{
mid=(l+r)/2;
if(fun_1(mid,y)<=0)
l=mid;
else if(fun_1(mid,y)>0) r=mid;
}
printf("%.4lf\n",fun_2(r,y));
}
return 0;
}
雄雄学长的生日到了!根据习俗,他需要将一些小蛋糕分给大家。已知他有 N 个不同口味、不同大小的蛋糕。有 F 个朋友会来参加雄雄学长的派对,每个人会拿到一块蛋糕 (必须是一个蛋糕的一块,不能由几个蛋糕的小块拼成;可以是一整个蛋糕)。
学长的朋友们都特别小气,如果有人拿到更大的一块,其他人就会开始抱怨。因此所有人拿到的蛋糕是同样大小的 (但不必是同样形状的),虽然这样有些蛋糕会被浪费,但总比搞砸整个派对好。当然,雄雄学长自己也要留一块,而这一块也要和其他人的同样大小。
请问每个人拿到的蛋糕最大是多少?( 每个蛋糕都是一个高为 1,半径不等的圆柱体。)
输入
首先输入一个整数 T,表示测试数据的组数。
对于每组测试数据:
第 1 行包含两个正整数 N, F,分别表示派的数量和朋友的数量,满足 1 <= N, F <= 10000。
第 2 行包含 N 个 1 到 10000 之间的整数,表示每个蛋糕的半径。
输出
每组测试数据对应一行,输出每个人能得到的最大的蛋糕的体积,误差不超过 10^(-3)。
AC代码:
//在wa了无数次后把pi从3.1415926改为acos(-1)居然过了....淦
#include
#include
#include
#include
#include
using namespace std;
const double pi=acos(-1.0);
double a[10010];
int n,f,r;
bool check(double x)
{
int cnt=0;
for(int i=1;i<=n;i++)
cnt+=(int)(a[i]/x);
if(cnt>=f+1) return 1;
else return 0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
scanf("%d%d",&n,&f);
for(int i=1;i<=n;i++)
{
scanf("%d",&r);
a[i]=pi*r*r;
}
sort(a+1,a+n+1);
double l=0,r=a[n],mid;
while(r-l>1e-6)
{
mid=(l+r)/2;
if(check(mid))
l=mid;
else r=mid;
}
printf("%.4lf\n",l);
}
return 0;
}
你暨被一个故意在日志中留名为雪糕怪物的黑客给黑了!进出校门光刷卡不行了,而被锁上了一个密码锁。聪明的学生温大卫与小旋破解了部分的密码,得到了一个长度为N(1≤N≤2000)的字符串S,然而最后的密码是由S中所有字母构成字典序最小的字符串T(起初T是一个空串)。但是要想知道最后的密码只能以下两种操作:
·从S的头部删除一个字符,加到T的尾部
·从S的尾部删除一个字符,加到T的尾部
目标是要构造字典序尽可能小的字符串。JNUACM的未来啊,你能不能帮帮温大卫和小旋呢?
Input
第一行一个整数N(代表字符串S的长度) 接下来的2~N+1行是字符串S中的字母(只包含大写字母)
Output
输出时每行最多80个字符
Sample Input
6
A
C
D
B
C
B
Sample Output
ABCBCD
AC代码:
#include
#include
#include
#include
using namespace std;
char str[2100];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>str[i];
int l=1,r=n;
int cnt=0;
while(n--)
{
if(str[l]<str[r]) cout<<str[l++];
else if(str[l]>str[r]) cout<<str[r--];
else
{
int i=l,j=r;
while(str[i]==str[j])
{
i++;j--;
}
if(str[i]<str[j]) cout<<str[l++];
else cout<<str[r--];
}
cnt++;
if(cnt%80==0) cout<<endl;
}
cout<<endl;
return 0;
}
一只青蛙想过河,但奈何体力不足,对着河犯难,所以请你帮它算一算。 这条河宽度是L,可以理解成一条长度为L(1<=l<=1e9)的直线,起点为0,终点为L。水流很急,一旦落水就会被冲走,不过好在河上有一些石头。石头有n(n <= 500000)个,呈一条直线排列。但由于体力不支,青蛙最多可以跳m次(1<= m <= n+1),也即最多选取n个石头中的m个作为自己的落脚点。 在选取完石头后,青蛙会对这个选取方案进行评估其跳跃难度,难度取决于相邻距离最远的两个石头,现在问所有跳跃方案中,难度最小的跳跃方案的难度是多少?(青蛙最长的跳跃距离)
Input
输入有多组样例,每组样例的第一行为L,n,m,下面n行描述n个石头的位置,位置用一个整数表示其距河岸起点的距离。不会出现两个石头的位置重合。
Output
每组样例请输出最小难度
AC代码:
#include
#include
#include
#include
using namespace std;
const int N=5e5+10;
int s,n,m,a[N];
bool check(int x)
{
int last=0,cnt=0,i;
for(i=1;i<n;i++)
{
if(a[i]-a[i-1]>x) return 0;
if(a[i]-a[last]<=x&&a[i+1]-a[last]>x)
{
cnt++;
last=i;
}
}
if(a[i]-a[i-1]>x) return 0;
cnt++;
if(cnt<=m) return 1;
else return 0;
}
int main()
{
while(scanf("%d%d%d",&s,&n,&m)!=EOF)
{
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
a[++n]=s;
sort(a,a+n+1);
int l=1,r=s,mid;
while(l<=r)
{
mid=(l+r)/2;
// cout<
if(check(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",r+1);
// cout<
}
return 0;
}
//0 2 6
//0 2 11 18 25
湫湫减肥
越减越肥!
最近,减肥失败的湫湫为发泄心中郁闷,在玩一个消灭免子的游戏。
游戏规则很简单,用箭杀死免子即可。
箭是一种消耗品,已知有M种不同类型的箭可以选择,并且每种箭都会对兔子造成伤害,对应的伤害值分别为Di(1 <= i <= M),每种箭需要一定的QQ币购买。
假设每种箭只能使用一次,每只免子也只能被射一次,请计算要消灭地图上的所有兔子最少需要的QQ币。
Input
输入数据有多组,每组数据有四行;
第一行有两个整数N,M(1 <= N, M <= 100000),分别表示兔子的个数和箭的种类;
第二行有N个正整数,分别表示兔子的血量Bi(1 <= i <= N);
第三行有M个正整数,表示每把箭所能造成的伤害值Di(1 <= i <= M);
第四行有M个正整数,表示每把箭需要花费的QQ币Pi(1 <= i <= M)。
特别说明:
1、当箭的伤害值大于等于兔子的血量时,就能将兔子杀死;
2、血量Bi,箭的伤害值Di,箭的价格Pi,均小于等于100000。
Output
如果不能杀死所有兔子,请输出”No”,否则,请输出最少的QQ币数,每组输出一行。
AC代码:
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int b[N];
struct node
{
int d,p;
//运算符重载小于号,优先队列本来默认大先出
//重载后价格小的在前
bool operator<(const node & o)const
{
return p>o.p;
}
}jian[N];
bool cmp_1(int x,int y)
{
return x>y;
}
bool cmp_2(node x,node y)
{
return x.d>y.d;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
sort(b+1,b+n+1,cmp_1);
// 兔子血量从大到小排列
for(int i=1;i<=m;i++) scanf("%d",&jian[i].d);
for(int i=1;i<=m;i++) scanf("%d",&jian[i].p);
sort(jian+1,jian+m+1,cmp_2);
// 伤害值从大到小排列
int ind=1;
// 箭的下标
priority_queue<node> q;
// 优先队列q用于存放可以杀死兔子的箭,且内部按价格从小到大排序
bool vis=0;
// 标记能否杀死所有兔子
long long ans=0;
for(int i=1;i<=n;i++)
{
while(jian[ind].d>=b[i]&&ind<=m)
// 只要箭的伤害值大于兔子的血量
{
q.push(jian[ind]);
ind++;
}
if(q.empty())
// 如果q是空的,说明剩余伤害值最大的箭都杀不死当前兔子
{
vis=1;
break;
}
else
{
ans+=q.top().p;
// 此时的top是所有可以杀死兔子的箭中价格最便宜的
q.pop();
// 就算有剩余,剩余的箭也一定能杀死后面的兔子
}
}
if(vis) printf("No\n");
else printf("%lld\n",ans);
}
return 0;
}
John has n points on the X axis, and their coordinates are (x[i],0),(i=0,1,2,…,n−1). He wants to know how many pairs that |x[b]−x[a]|≤k.(a Input
The first line contains a single integer T (about 5), indicating the number of cases.
Each test case begins with two integers n,k(1≤n≤100000,1≤k≤109).
Next n lines contain an integer x[i (−109≤x[i]≤109), means the X coordinates.
Output
For each case, output an integer means how many pairs that |x[b]−x[a]|≤k.
Sample Input
2
5 5
-100
0
100
101
102
5 300
-100
0
100
101
102
Sample Output
3
10
#include
#include
using namespace std;
typedef long long ll;
int a[100010];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
ll ans=0;
// -100 0 100 101 102
// i=2 l=3 r=4 m=3 l=4 m=4 l=5
for(int i=0;i<n;i++) //枚举x[a]
{
int l=i+1,r=n-1,mid,t=0;
while(l<=r) //二分查找x[b]使x[b]-x[a]=k
{
mid=(l+r)/2;
if((ll)a[mid]-a[i]<=k)
l=mid+1;
else r=mid-1;
}
ans=ans+l-i-1;
// cout<
}
cout<<ans<<endl;
}
return 0;
}