6.二分+贪心

二分查找

二分查找的基本思想:
将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x。
时间复杂度:O(log2(n))

贪心算法

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。

例如,平时购物找零钱时,为使找回的零钱的硬币数最少,不要求找零钱的所有方案,而是从最大面值的币种开始,按递减的顺序考虑各面额,先尽量用大面值的面额,当不足大面值时才去考虑下一个较小面值,这就是贪心算法 。

练习

进击的奶牛 (二分)洛谷P1824

题目描述
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 <iostream>
#include <algorithm>
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;
}

A - Trailing Zeroes (III)(判断n!末尾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 <iostream>
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;
}

B - Strange fuction(二分)

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 <iostream>
#include <algorithm>
#include <cmath>
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;
}

C - Pie(二分)⭐⭐

雄雄学长的生日到了!根据习俗,他需要将一些小蛋糕分给大家。已知他有 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 <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
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;
}

D - Best Cow Line(贪心)

你暨被一个故意在日志中留名为雪糕怪物的黑客给黑了!进出校门光刷卡不行了,而被锁上了一个密码锁。聪明的学生温大卫与小旋破解了部分的密码,得到了一个长度为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 <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
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;
}

E - The Frog’s Games(二分)

一只青蛙想过河,但奈何体力不足,对着河犯难,所以请你帮它算一算。 这条河宽度是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 <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
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<<mid<<" "<<check(mid)<<endl;
			if(check(mid))	r=mid-1;
			else	l=mid+1;
		}
		printf("%d\n",r+1);
//		cout<<r+1<<endl;
	}
	return 0;
}
//0 2 6
//0 2 11 18 25

F - 湫湫系列故事——消灭兔子(贪心+运算符重载)⭐⭐

湫湫减肥
  越减越肥!
  
  最近,减肥失败的湫湫为发泄心中郁闷,在玩一个消灭免子的游戏。
  游戏规则很简单,用箭杀死免子即可。
  箭是一种消耗品,已知有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 <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
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;
}

G - pairs

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<a,b> that |x[b]−x[a]|≤k.(a<b)
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<a,b> 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 <iostream>
#include <algorithm>
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<<i<<":"<<l-i-1<<endl;
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二分贪心算法和浙江工业大学之间似乎没有直接的关联。二分贪心算法是一种综合了二分贪心思想的算法,用于在解决问题时对数据进行二分划分,并在每一步选择中采取最优的贪心策略。而浙江工业大学是一所位于中国浙江省的工科大学。 引用和中提到的贪心算法是一种在问题求解中每一步都选择最优的策略,但得到的结果不一定是最优解,但是通常是相对近似最优解的结果。这种算法在某种意义上是局部最优解。 引用中提到的Dijkstra算法是一种基于贪心和广度优先搜索的算法,用于求解图中一个点到其他所有点的最短路径。该算法的时间复杂度为O(n^2)。 从以上内容来看,二分贪心算法和浙江工业大学之间并没有明显的联系。可能是问题描述不完整或者存在其他相关信息,导致无法直接回答与贪心算法和浙江工业大学有关的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [常用十大算法 非递归二分查找、分治法、动态规划、贪心算法、回溯算法(骑士周游为例)、KMP、最小生成树算法...](https://blog.csdn.net/lonelysnowman/article/details/127493659)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [6.二分+贪心](https://blog.csdn.net/qq_52008247/article/details/119151264)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值