分数规划 学习笔记

貌似这玩意集训的时候讲过 但我完全不会 怎么会事呢(?)

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;
}

P3199 [HNOI2009] 最小圈

这题做过还不会 鉴定为贺的

题意:求图中边权平均值最小的环

设边权为 $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;
}

ybtoj 最小生成树I.重建小镇

题意:\(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;
}
posted @ 2023-10-12 11:38  xiang_xiang  阅读(5)  评论(0编辑  收藏  举报