分数规划 学习笔记
貌似这玩意集训的时候讲过 但我完全不会 怎么会事呢(?)
01分数规划可用于求解如下问题:
给出 \(a_i ,b_i\) 求 \(\dfrac{\sum a_i\times s_i}{\sum b_i\times s_i} (s_i\in \text{{0,1}})\) 的最大值/最小值
考虑二分答案 \(mid :\)
\(\dfrac{\sum a_i\times s_i}{\sum b_i\times s_i}>mid\)
\(\sum a_i\times s_i>mid\times \sum b_i\times s_i\)
\(\sum a_i\times s_i-mid\times \sum b_i\times s_i>0\)
\(\sum s_i\times (a_i-mid\times b_i)>0\)
判断能否满足左侧柿子能否大于0即可
实际上可以看做一个物品选/不选(可贪心/背包 等)
例:P4377 [USACO18OPEN] Talent Show G
题意:求\(\dfrac{\sum t_i\times s_i}{\sum w_i\times s_i} (s_i\in \text{{0,1}})\) 最大值 且要求 \(\sum w_i\geq W\)
相比原问题 多了一个重量和比小于 \(W\) 的限制
那不就是01背包吗
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define int ll
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e6+5;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'|c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,W,t[N],w[N],f[N],ans;
inl bool check(int mid){
for(int i=1;i<=W;i++)f[i]=-inf;
for(int i=1;i<=n;i++){
for(int j=W;~j;j--){
if(j+w[i]>=W)f[W]=max(f[W],f[j]+t[i]-mid*w[i]);
else f[j+w[i]]=max(f[j+w[i]],f[j]+t[i]-mid*w[i]);
}
}
return f[W]>0;
}
signed main(){
n=read();W=read();
for(int i=1;i<=n;i++)w[i]=read(),t[i]=read()*1000;
int l=0,r=1e6;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
ans=mid;
l=mid+1;
}else r=mid-1;
}
writel(ans);
return 0;
}
这题做过还不会 鉴定为贺的
题意:求图中边权平均值最小的环
设边权为 $a_i $ 点权为 \(b_i\)(均为1) 那么其实是求环中 \(\dfrac{\sum a_i}{\sum b_i}\) 的最小值
二分一个 \(mid\) 那么我们就是要判 \(\sum(a_i-mid\times b_i)<0\) 是否存在
\(b_i=1\) 那么判 \(\sum (a_i-mid)<0\) 是否存在 spfa判负环即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-10;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'|c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,u,v;
double c,w[N];
int head[N],nxt[N],to[N],p[N],cnt;
inl void add(int u,int v,double c,int k){
nxt[++cnt]=head[u];
to[cnt]=v;w[cnt]=c;p[cnt]=k;
head[u]=cnt;
}
int vis[N],tot[N];
double dis[N];
inl bool spfa(double mid){
queue<int>q;
for(int i=1;i<=n;i++)dis[i]=inf,vis[i]=0;
dis[0]=0;q.push(0);
while(!q.empty()){
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=nxt[i]){
int y=to[i];double c=w[i]-mid*p[i];
if(dis[y]>dis[x]+c){
dis[y]=dis[x]+c;
tot[y]=tot[x]+1;
if(tot[y]>n){return 1;}
if(!vis[y]){q.push(y);vis[y]=1;}
}
}
}
return 0;
}
signed main(){
n=read();m=read();
for(int i=1;i<=m;i++){
u=read();v=read();
scanf("%lf",&c);
add(u,v,c,1);
}
for(int i=1;i<=n;i++)add(0,i,0,0);
double l=-1e4,r=1e4;
while(r-l>eps){
double mid=(l+r)/2;
if(spfa(mid))r=mid;
else l=mid;
}
printf("%.8lf\n",l);
return 0;
}
题意:\(m\) 条边选几条边 使图联通 求\(\dfrac{f-\sum c_i\times s_i}{\sum t_i\times s_i} (s_i\in \text{{0,1}},c_i、 t_i为边的属性)\) 最大值
二分 求 \(f-\sum(c_i+mid\times t_i)>0\) 最大值
使 \(\sum(c_i+mid\times t_i)\)最小并使图联通 跑最小生成树即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-6;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'|c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,f,u[N],v[N],c[N],t[N],fa[N];
inl int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct edge{
int u,v;
double c;
friend bool operator<(edge a,edge b){return a.c<b.c;}
}e[N];
inl bool check(double mid){
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
e[i]={u[i],v[i],c[i]+mid*t[i]};
sort(e+1,e+m+1);
double sum=0;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
int fu=find(u),fv=find(v);
double c=e[i].c;
if(fu==fv)continue;
sum+=c;fa[fu]=fv;
}
return f-sum>0;
}
signed main(){
n=read();m=read();f=read();
for(int i=1;i<=m;i++)
u[i]=read(),v[i]=read(),c[i]=read(),t[i]=read();
double l=0,r=2e9;
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)){
l=mid;
}else r=mid;
}
printf("%.4lf\n",l);
return 0;
}