专题 | 二分&0/1分数规划&数学期望

二分

二分编号

对于一个有单调性的数组,我们可以二分编号,如果a[mid]<ans,那么mid就往a[]更大的那边走

while(r>l){
    mid=(l+r)/2;
    if(judge(mid))l=mid;
    else r=mid;
}

注意二分代码的写法细节和你的check函数有关!(其实是和你在对于恰好满足要求时check返回值有关)

二分答案

二分答案思路:二分一个猜测的答案mid,进行判定,如果计算出真正的答案q不能达到此mid(a-mid<0)mid就变小(mid=l)

否则mid=r

下面是实数域的二分答案。如果只是整数域,则和上面的无异(参考https://www.luogu.com.cn/problem/P1873)。

double l=?,r=?,mid;
while(r-l>=0.00001){
    mid=(l+r)/2;
    if(sum(mid)>=0)l=mid;
    else r=mid;
}

无论何种二分,请明确二分的上下界(l,r的初始值)

三分

题目描述

给定 n n n 个二次函数 f 1 ( x ) , f 2 ( x ) , … , f n ( x ) f_1(x),f_2(x),\dots,f_n(x) f1(x),f2(x),,fn(x)(均形如 a x 2 + b x + c ax^2+bx+c ax2+bx+c),设 F ( x ) = max ⁡ { f 1 ( x ) , f 2 ( x ) , . . . , f n ( x ) } F(x)=\max\{f_1(x),f_2(x),...,f_n(x)\} F(x)=max{f1(x),f2(x),...,fn(x)},求 F ( x ) F(x) F(x) 在区间 [ 0 , 1000 ] [0,1000] [0,1000] 上的最小值。

输入格式

输入第一行为正整数 T T T,表示有 T T T 组数据。

每组数据第一行一个正整数 n n n,接着 n n n 行,每行 3 3 3 个整数 a , b , c a,b,c a,b,c,用来表示每个二次函数的 3 3 3 个系数,注意二次函数有可能退化成一次。


虽然精度要求不高,但是eps还是要足够小!(在复杂度范围内)

注意加减eps若干倍取m1,m2会TLE

const double eps=1e-12;//精度开高!
int a[N],b[N],n,c[N];

double f(double x,int i){
	return x*x*a[i]+x*b[i]+c[i];
}
double check(double x){
	double res=f(x,1);
	for(int i=2;i<=n;i++)res=max(res,f(x,i));
	return res;
}

signed main(){
	int T=rd;
	// cerr<<eps<<endl;
	while(T--){
		n=rd;
		for(int i=1;i<=n;i++)a[i]=rd,b[i]=rd,c[i]=rd;
		double l=0,r=INF;
		while(r-l>eps){
			double m1=l+(r-l)/3.0;
			double m2=r-(r-l)/3.0;
			if(check(m1)>check(m2))l=m1;
			else r=m2;
			// cerr<<m1<<' '<<m2<<' '<<l<<' '<<r<<endl;
		}
		// cerr<<"l="<<l<<endl;
		printf("%.4lf\n",check(l));

	}
}

分数规划

下图中划红线的不等式重要!!

分数规划多和其他知识点结合考察。以下选举例题讲解

[USACO18OPEN] Talent Show G

Farmer John 要带着他的 n n n 头奶牛,方便起见编号为 1 … n 1\ldots n 1n,到农业展览会上去,参加每年的达牛秀!他的第 i i i 头奶牛重量为 w i w_i wi,才艺水平为 t i t_i ti,两者都是整数。

在到达时,Farmer John 就被今年达牛秀的新规则吓到了:

(一)参加比赛的一组奶牛必须总重量至少为 W W W(这是为了确保是强大的队伍在比赛,而不仅是强大的某头奶牛),并且。

(二)总才艺值与总重量的比值最大的一组获得胜利。

FJ 注意到他的所有奶牛的总重量不小于 W W W,所以他能够派出符合规则(一)的队伍。帮助他确定这样的队伍中能够达到的最佳的才艺与重量的比值。

输入格式

第一行是两个整数,分别表示牛的个数 n n n 和总重量限制 W W W

2 2 2 ( n + 1 ) (n+1) (n+1) 行,每行两个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数表示第 i i i 头奶牛的重量 w i w_i wi 和才艺水平 t i t_i ti

输出格式

请求出 Farmer 用一组总重量最少为 W W W 的奶牛最大可能达到的总才艺值与总重量的比值。

如果你的答案是 A A A,输出 1000 A 1000A 1000A 向下取整的值,以使得输出是整数(当问题中的数不是一个整数的时候,向下取整操作在向下舍入到整数的时候去除所有小数部分)。

样例 #1

样例输入 #1

3 15
20 21
10 11
30 31

样例输出 #1

1066

提示

样例解释

在这个例子中,总体来看最佳的才艺与重量的比值应该是仅用一头才艺值为 11 11 11、重量为 10 10 10 的奶牛,但是由于我们需要至少 15 15 15 单位的重量,最优解最终为使用这头奶牛加上才艺值为 21 21 21、重量为 20 20 20 的奶牛。这样的话才艺与重量的比值为 11 + 21 10 + 20 = 32 30 = 1.0666 … \frac{11+21}{10+20}=\frac{32}{30} = 1.0666\dots 10+2011+21=3032=1.0666,乘以1000向下取整之后得到 1066 1066 1066

数据规模与约定

对于全部的测试点,保证 1 ≤ n ≤ 250 1 \leq n \leq 250 1n250 1 ≤ W ≤ 1000 1 \leq W \leq 1000 1W1000 1 ≤ w i ≤ 1 0 6 1 \leq w_i \leq 10^6 1wi106 1 ≤ t i ≤ 1 0 3 1 \leq t_i \leq 10^3 1ti103

Solu

注意1:二分上下界

因为 1 ≤ w i ≤ 1 0 6 1 \leq w_i \leq 10^6 1wi106 1 ≤ t i ≤ 1 0 3 1 \leq t_i \leq 10^3 1ti103,故 t i t_i ti 最大取 1000 1000 1000 , w i w_i wi 最小取 1 1 1 ,得到二分上界 r = 1000 / 1 = 1000 r=1000/1=1000 r=1000/1=1000

注意2:r-l的精度

因为本题要求结果乘以1000向下取整,因此至少要精确到 1 0 − 4 10^{-4} 104

注意3:sum函数返回r而不是l

这与题目要求有关.如果题目要求答案四舍五入,那么两个均可.但本题要求*1000后向下取整,就会出问题

因此统一返回 r 即可.精度越高越好,但考虑复杂度.乘以1000后取整,精度可以取 1 0 − 5 10^{-5} 105

Code

/*ACACACACACACAC///
Code By Ntsc
/*ACACACACACACAC///
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e3;

int n,k,W;
int m,f[N],ans;
double tt[N];
int t[N],w[N];
double sum(double x){
	for(int i=1;i<=W;i++)tt[i]=-1e9;
	for(int i=1;i<=n;i++){
		for(int j=W;j>=0;j--){//倒序枚举第2位,可以优化掉第2维(滚动数组)
			int k=min(W,j+w[i]);//若w[i]+j(即原有总重量+目前要加上的重量)超过W,我们也把它看作W
			tt[k]=max(tt[k],tt[j]+t[i]-x*w[i]);
		}
	}
	return tt[W];
}
signed main(){
	cin>>n>>W;
	for(int i=1;i<=n;i++)cin>>w[i]>>t[i];
	
	double l=0,r=1000,mid;
	while(r-l>=0.00001){
		mid=(l+r)/2;
		if(sum(mid)>=0)l=mid;
		else r=mid;
	}
	printf("%d",int(r*1000));
	return 0;
}

Dropping tests

给n组数据 a i a_i ai , b i b_i bi,定义累计平均值为:

https://images2017.cnblogs.com/blog/740857/201709/740857-20170909154148882-1115891769.png

现给出一个整数 k k k,要求从这 n n n 个数中去掉 k k k 个数后,最大累计平均值能有多大?(四舍五入到整数)

Description

In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be

https://images2017.cnblogs.com/blog/740857/201709/740857-20170909154148882-1115891769.png

Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.

http://poj.org/images/2976_2.gif

Input

The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ aibi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.

Output

For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.

Sample Input

3 1
5 0 2
5 1 6
4 2
1 2 7 9
5 6 7 9
0 0

Sample Output

83
100

Hint

To avoid ambiguities due to rounding errors, the judge tests have been constructed so that all answers are at least 0.001 away from a decision boundary (i.e., you can assume that the average is never 83.4997).

Code

/*ACACACACACACAC///
Code By Ntsc
/*ACACACACACACAC///
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5;

int n,k;
double m,b[N],ans,a[N],w[N];
double sum(double x){
	for(int i=1;i<=n;i++)w[i]=1.0*a[i]-x*b[i];
	sort(w+1,w+n+1);
	double tmp=0;
	for(int i=n;i>=k+1;i--){
		tmp+=w[i];
	}
//	for(int i=k+1;i<=n;i++){
//		tmp+=w[i];
//	}
	return tmp;
}
signed main(){
	while(1){
		cin>>n>>k;
		if(!n&&!k)return 0;
		for(int i=1;i<=n;i++)cin>>a[i];
		for(int i=1;i<=n;i++)cin>>b[i];
	
	
		double l=0,r=1,mid;
		while(r-l>0.0001){
			mid=(l+r)/2;
			if(sum(mid)>=0)l=mid;
			else r=mid;
		}
		printf("%.0lf\n",100*l);
	}
	return 0;
}

注意

1.循环别写错了

//	for(int i=n;i>=n-k;i--){
//		tmp+=w[i];
//	}
	for(int i=k+1;i<=n;i++){
		tmp+=w[i];
	}

2.由取值范围决定二分上下界

0 ≤ a i ≤ b i ≤ 1 , 000 , 000 , 000 0 ≤ ai ≤ bi ≤ 1, 000, 000, 000 0aibi1,000,000,000,因为*$ a_i ≤ b_i$,故和的最大值为1*

Desert King

题意:给n个 (n≤10000) 位置的二维坐标(x,y) 和海拔 h ,定义两点通道长度为二维坐标的欧几里得距离,两点修通道的花费是两点的海拔之差,要求修n-1条水管形成一个生成树,使得通道总花费与通道总长度的比率最小。

这道题是经典的最优比率生成树问题

David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way.

After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital.

His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can’t share a lifter. Channels can intersect safely and no three villages are on the same line.

As King David’s prime scientist and programmer, you are asked to find out the best solution to build the channels.

Input

There are several test cases. Each test case starts with a line containing a number N (2 <= N <= 1000), which is the number of villages. Each of the following N lines contains three integers, x, y and z (0 <= x, y < 10000, 0 <= z < 10000000). (x, y) is the position of the village and z is the altitude. The first village is the capital. A test case with N = 0 ends the input, and should not be processed.

Output

For each test case, output one line containing a decimal number, which is the minimum ratio of overall cost of the channels to the total length. This number should be rounded three digits after the decimal point.

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000

solution

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fflowus.cn%2Fpreview%2Facf67ce8-9782-4832-8786-f123f453dcfd&pos_id=img-2BvvFZPn-1710640372680

补充知识:最小生成树

算法prim

code

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fflowus.cn%2Fpreview%2F26bb55a2-3155-464a-bd3c-b6a1cabd4463&pos_id=img-4fnyIQOB-1710640372680

错误的代码WA

/*ACACACACACACAC///
       . Code by Ntsc .
       . Love by Liye .
/*ACACACACACACAC///

#include<bits/stdc++.h>
#define ll long long
#define db double
#define rtn return
using namespace std;

const int N=1e4+5;
const int M=1e5;
const int Mod=1e5;
const int INF=1e9;

int n,m,ans;
double a[N][N],b[N][N];//存高度差和水平距离 
int z[N],x[N],y[N],vis[N],d[N];

db getb(int u,int v){//计算距离(水平距离) 
	return sqrt(1.0*(x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]));
}
int geta(int u,int v){
	return abs(z[u]-z[v]);
}
bool prim(db x){
	memset(vis ,0,sizeof vis);
	for(int i=0;i<=n;i++){
		d[i]=INF;
	}
	d[1]=0;
	db sum=0;
	for(int i=1;i<=n;i++){
		int t=0;
		for(int j=1;j<=n;j++){
			if(!vis[j]&&d[j]<d[t])t=j;	
		}
		sum+=d[t];vis[t]=1;
		for(int j=1;j<=n;j++)if(!vis[j]&&a[t][j]-x*b[t][j]<d[j])d[j]=a[t][j]-x*b[t][j];
	}
	return sum<=0;
}
db find(){
	db l=0,r=1e7,mid;
	while(l-r>1e-6){
		mid=(l+r)/2;
		if(prim(mid))r=mid;//可以更小 
		else l=mid;//不可以更小 
	}
	return r;
}
signed main(){
	while(1){
		cin>>n;
		if(!n)return 0;
		for(int i=1;i<=n;i++)cin>>x[i]>>y[i]>>z[i];
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)a[i][j]=geta(i,j),b[i][j]=getb(i,j);
		}
		printf("%.3lf\n",find());
	}
	return 0;
}

[HNOI2009]最小圈

考虑带权的有向图 G = ( V , E ) G=(V,E) G=(V,E)以及 w : E → R w:E\rightarrow R w:ER,每条边 e = ( i , j ) ( i ≠ j , i ∈ V , j ∈ V ) e=(i,j)(i\neq j,i\in V,j\in V) e=(i,j)(i=j,iV,jV)的权值定义为 w i , j w_{i,j} wi,j,令 n = ∣ V ∣ n=|V| n=V c = ( c 1 , c 2 , ⋯   , c k ) ( c i ∈ V ) c=(c_1,c_2,\cdots,c_k)(c_i\in V) c=(c1,c2,,ck)(ciV) G G G中的一个圈当且仅当 ( c i , c i + 1 ) ( 1 ≤ i < k ) (c_i,c_{i+1})(1\le i<k) (ci,ci+1)(1i<k) ( c k , c 1 ) (c_k,c_1) (ck,c1)都在 E E E中,这时称 k k k为圈 c c c的长度同时令 c k + 1 = c 1 c_{k+1}=c_1 ck+1=c1,并定义圈 c = ( c 1 , c 2 , ⋯   , c k ) c=(c_1,c_2,\cdots,c_k) c=(c1,c2,,ck)的平均值为 μ ( c ) = ∑ i = 1 k w c i , c i + 1 / k \mu(c)=\sum\limits_{i=1}^{k} w_{c_i,c_{i+1}}/k μ(c)=i=1kwci,ci+1/k,即 c c c上所有边的权值的平均值。令 μ ′ ( c ) = M i n ( μ ( c ) ) \mu'(c)=Min(\mu(c)) μ(c)=Min(μ(c)) G G G中所有圈 c c c的平均值的最小值。现在的目标是:在给定了一个图 G = ( V , E ) G=(V,E) G=(V,E)以及 w : E → R w:E\rightarrow R w:ER之后,请求出 G G G中所有圈 c c c的平均值的最小值 μ ′ ( c ) = M i n ( μ ( c ) ) \mu'(c)=Min(\mu(c)) μ(c)=Min(μ(c))输入格式

第一行2个正整数,分别为 n n n m m m,并用一个空格隔开,只用 n = ∣ V ∣ , m = ∣ E ∣ n=|V|,m=|E| n=V,m=E分别表示图中有 n n n个点 m m m条边。
接下来m行,每行3个数 i , j , w i , j i,j,w_{i,j} i,j,wi,j,表示有一条边 ( i , j ) (i,j) (i,j)且该边的权值为 w i , j w_{i,j} wi,j。输入数据保证图 G = ( V , E ) G=(V,E) G=(V,E)连通,存在圈且有一个点能到达其他所有点。

输出格式

请输出一个实数 μ ′ ( c ) = M i n ( μ ( c ) ) \mu'(c)=Min(\mu(c)) μ(c)=Min(μ(c)),要求输出到小数点后8位。

对于100%的数据, n ≤ 3000 , m ≤ 10000 , ∣ w i , j ∣ ≤ 1 0 7 n\le 3000,m\le 10000,|w_{i,j}| \le 10^7 n3000,m10000,wi,j107

题意

有向图中找圈。定义c为改圈的边权和/边数,在所有c中求出最小的那个c

code AC

请注意边权请使用double存,包括 邻接表中 和d[]数组

/*ACACACACACACAC///
       . Code by Ntsc .
       . Love by Liye .
/*ACACACACACACAC///

#include<bits/stdc++.h>
#define ll long long
#define db double
#define rtn return
using namespace std;

const int N=1e4+5;
const int M=1e5;
const int Mod=1e5;
const int INF=1e9;

int n,m,ans;
double a[N][N],b[N][N];//存高度差和水平距离 
int x[N],y[N],vis[N];
db d[N];
int u,v;


struct edge{
	int v;
	db c;
	int nxt;
}e[N];
int h[N],idx;
void add(int a,int b,db w){
	e[++idx]={b,w,h[a]};
	h[a]=idx;
}
bool spfa(int u,db x){
	vis[u]=1;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(d[v]>d[u]+e[i].c-x){
			d[v]=d[u]+e[i].c-x;
			if(vis[v]||spfa(v,x))return 1;//vis[]为true表示走到了之前走过的点,说明有负环 spfa表示继续往下走 
		}
	}
	vis[u]=0;
	return 0;
}
bool check(db x){
	memset(d,0x3f,sizeof d);
	memset (vis,0,sizeof vis);
	for(int i=1;i<=n;i++){//不保证每个点都能到达其他所有点(原句"存在圈且有一个点能到达其他所有点",所以需要每个点都找一遍spfa 
		if(spfa(i,x))return 1;
	}
	return 0;
}
db find(){
	db l=-1e7,r=1e7,mid;
	while(r-l>1e-9){
		mid=(l+r)/2;
		if(check(mid))r=mid;
		else l=mid;
	}
	return r;
}
signed main(){
	cin>>n>>m;
	int u,v;
	db w;
	for(int i=1;i<=m;i++){
		cin>>u>>v>>w;
		add(u,v,w);
	}
	printf("%.8lf",find());
	return 0;
}

复杂度分析

你已经长大了,啥时候才能自己计算复杂度呀?

O ( n m × l o g ( 1 e 16 ) ) O(nm\times log(1e16)) O(nm×log(1e16))

nm就是一次check的复杂度,其中m是一次spfa的复杂度

log(1e17)是二分答案的复杂度(算法:对上下界的差除以精度所得的商进行log)

分数规划&依赖背包

练习 | 这人怎么天天刷题啊’

www.luogu.com.cn

数学期望

hole

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值