【学习笔记】01 分数规划
分数规划问题,大概就是一类求解分式最值的问题。
比如下面这个问题:给定
当然还可能有一些奇怪的其他要求,比如限制分子分母大小之类的。
下文将会选几个最简单的此种类型题目进行分析。
二分求解
一般情况下,求解此类问题最常见的方法就是二分答案。
就一上面提到的问题举例,考虑去二分这个最小值,将其记为
这样问题就变得十分简单了,相当于对于每个物品将
Dinkelbach 算法
嗯?这是个什么东西?问我?我也不知道。
确实没具体了解过,这里就先引用下 OI Wiki 上解释的:
Dinkelbach 算法的大概思想是每次用上一轮的答案当做新的 L 来输入,不断地迭代,直至答案收敛。
一些例题
尽量选了些有营养的,避免重复。
洛谷 P1570 KC 喝咖啡#
简要题意:有
入门题,考虑直接使用上面讲的套路,二分出
注意这里是最大化,因此是大于等于
时间复杂度:
代码是很久之前写的了,所以非常丑,那时还没有火车头,码风跟现在相比完全看不出来是同一个人写的:
int n,m,v[205],c[205];
double t[205],ans,l,r;
bool cmp(double a,double b)
{
return a>b;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i];
for(int i=1;i<=n;i++)
cin>>c[i],r+=v[i];
while(r-l>1e-6)
{
double mid=(l+r)/2.0,ans=0;
for(int i=1;i<=n;i++) t[i]=v[i]-c[i]*mid;
sort(t+1,t+n+1,cmp);
for(int i=1;i<=m;i++) ans+=t[i];
if(ans>=0) l=mid;
else r=mid;
}
cout<<fixed<<setprecision(3)<<l;
}
洛谷 P4377 [USACO18OPEN] Talent Show G#
简要题意:有
同样还是先根据套路,二分
考虑如何解决
时间复杂度
int n,m,w[N],t[N];
db f[N],v[N];
bool check(db mid){
For(i,1,n) v[i]=(db)t[i]-mid*w[i];
For(i,1,m) f[i]=-inf;
f[0]=0;
For(i,1,n) Rof(j,m,0){
if(j+w[i]>=m) f[m]=max(f[m],f[j]+v[i]);
else f[j+w[i]]=max(f[j+w[i]],f[j]+v[i]);
}
return f[m]>=0;
}
void Main(){
read(n,m);
For(i,1,n) read(w[i],t[i]);
db l=0,r=1e6;
while(r-l>1e-6){
db mid=(l+r)/2.0;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d",int(l*1000));
}
洛谷 P4322 [JSOI2016]最佳团体#
题意:有
二分
这里的树上背包要求通过不断合并子树进行更新,枚举量时刻不超过当前已并入的子树大小之和,可以证明这样时间复杂度为 虽然我不会证明。
时间复杂度:
struct Edge{
int nxt,to;
}e[N];
int n,k,cnt,head[N],s[N],p[N],fa[N];
int w[N],v[N],siz[N],f[N][N];
void add_edge(int u,int v){
e[++cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void dfs(int now){
f[now][1]=v[now];siz[now]=1;
for(int i=head[now];i;i=e[i].nxt){
int to=e[i].to;
if(to==fa[now]) continue;
dfs(to);siz[now]+=siz[to];
Rof(j,siz[now],1) For(k,0,min(j-1,siz[to]))
f[now][j]=max(f[now][j],f[now][j-k]+f[to][k]);
}
}
bool check(db x){
For(i,0,n) v[i]=(db)p[i]-x*s[i];
memset(f,-0x3f,sizeof(f));
dfs(0);return f[0][k+1]>=0;
}
void Main(){
read(k,n);
For(i,1,n){
read(s[i],p[i],fa[i]);
add_edge(fa[i],i);
}fa[0]=-1;
db l=0.0,r=114514.0;
while(r-l>1e-6){
db mid=(l+r)/2.0;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3lf",l);
}
POJ 2728 Desert King#
题意:给你
二分
考虑是完全图,因此使用 Prim 算法求最小生成树。
时间复杂度:
代码因为特别的原因,不放了。
洛谷 P3199 [HNOI2009]最小圈#
题意:给你一张
考虑对于途中的每一个点定义一个点权
二分答案
由于所有的
对于任意的
时间复杂度:
struct Edge{
int nxt,to;
db val;
Edge(int nxt=0,int to=0,db val=0):nxt(nxt),to(to),val(val){}
}e[N];
int n,m,cnt,head[N];
db dis[N];
bool vis[N];
void add_edge(int u,int v,db w){
e[++cnt]=Edge(head[u],v,w);
head[u]=cnt;
}
bool spfa(int now,db mid){
vis[now]=1;
for(int i=head[now];i;i=e[i].nxt){
int to=e[i].to;
if(dis[to]>dis[now]+e[i].val-mid){
dis[to]=dis[now]+e[i].val-mid;
if(vis[to]||spfa(to,mid)) return 1;
}
}
vis[now]=0;
return 0;
}
bool check(db mid){
For(i,1,n) vis[i]=dis[i]=0;
For(i,1,n) if(spfa(i,mid))
return 1;
return 0;
}
void Main(){
read(n,m);
For(i,1,m){
int u,v;db w;
scanf("%d%d%lf",&u,&v,&w);
add_edge(u,v,w);
}
db l=-1e7,r=1e7;
while(r-l>eps){
db mid=(l+r)/2.0;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.8lf",r);
}
洛谷 P3705 [SDOI2017]新生舞会#
二分后直接转化为带权二分图匹配,费用流即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!