三分/01分数规划

三分

最小球覆盖 2018南京D
三分套三分套三分

constexpr int N=105;
struct node{
    int x,y,z;
}a[N];
int n;
double road(double x1,double y1,double z1,double x2,double y2,double z2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2));
}
double check(double x,double y,double z){
    double maxn=0;
    for (int i=1;i<=n;i++){
        maxn=std::max(maxn,road(x,y,z,a[i].x,a[i].y,a[i].z));
    }
    return maxn;
}
double check(double x,double y){
    double l=-100000,r=100000,ans=1e18;
    for (int i=1;i<=80;i++){
        double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
        double res1=check(x,y,mid1),res2=check(x,y,mid2);
        if (res1<res2){
            r=mid2;
        }else{
            l=mid1;
        }
        ans=std::min(ans,std::min(res1,res2));
    }
    return ans;
}
double check(double x){
    double l=-100000,r=100000,ans=1e18;
    for (int i=1;i<=80;i++){
        double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
        double res1=check(x,mid1),res2=check(x,mid2);
        if (res1<res2){
            r=mid2;
        }else{
            l=mid1;
        }
        ans=std::min(ans,std::min(res1,res2));
    }
    return ans;
}
void yrzr(){
    std::cin>>n;
    for (int i=1;i<=n;i++){
        std::cin>>a[i].x>>a[i].y>>a[i].z;
    }
    double l=-100000,r=100000,ans=1e18;
    for (int i=1;i<=80;i++){
        double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
        // std::cout<<std::fixed<<std::setprecision(5)<<mid1<<" "<<mid2<<"\n";
        double res1=check(mid1),res2=check(mid2);
        if (res1<res2){
            r=mid2;
        }else{
            l=mid1;
        }
        ans=std::min(ans,std::min(res1,res2));
    }
    std::cout<<std::fixed<<std::setprecision(5)<<ans;
}

P2571 [SCOI2010] 传送带
感性理解一下,从 a b ab ab传送带到 c d cd cd传送带,每个传送带只有一个点出发/到达是最优的,所以单峰,满足三分性

int ax,ay,bx,by,cx,cy,dx,dy;
int p,q,r;
double road(double x1,double y1,double x2,double y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double check(double x,double y){
    double lx=cx,ly=cy,rx=dx,ry=dy,ans=1e18;
    for (int i=1;i<=100;i++){
        double mid1x=(2*lx+rx)/3,mid1y=(2*ly+ry)/3;
        double mid2x=(lx+2*rx)/3,mid2y=(ly+2*ry)/3;
        double res1=road(dx,dy,mid1x,mid1y)/q+road(x,y,mid1x,mid1y)/r;
        double res2=road(dx,dy,mid2x,mid2y)/q+road(x,y,mid2x,mid2y)/r;
        if (res1>res2){
            lx=mid1x;
            ly=mid1y;
        }else{
            rx=mid2x;
            ry=mid2y;
        }
        ans=std::min(ans,res1);
    }
    return ans;
}
void yrzr(){
    std::cin>>ax>>ay>>bx>>by>>cx>>cy>>dx>>dy>>p>>q>>r;
    double lx=ax,ly=ay,rx=bx,ry=by,ans=1e18;
    for (int i=1;i<=100;i++){
        double mid1x=(2*lx+rx)/3,mid1y=(2*ly+ry)/3;
        double mid2x=(lx+2*rx)/3,mid2y=(ly+2*ry)/3;
        double res1=road(ax,ay,mid1x,mid1y)/p+check(mid1x,mid1y);
        double res2=road(ax,ay,mid2x,mid2y)/p+check(mid2x,mid2y);
        if (res1>res2){
            lx=mid1x;
            ly=mid1y;
        }else{
            rx=mid2x;
            ry=mid2y;
        }
        ans=std::min(ans,res1);
    }
    std::cout<<std::fixed<<std::setprecision(2)<<ans;
}

[SHOI2017]期末考试
从小到大排序,三分最后出成绩的时间,然后贪心,将晚出的学科进行操作
时间复杂度通过前缀和预处理可以做到 O ( n l o g n + l o g 2 n ) O(nlogn+log^2n) O(nlogn+log2n)

注意测试点C==1e16时会炸longlong,进行特判

constexpr int N=1e5+5;
int A,B,C,n,m;
int t[N],b[N],sum[N],tol[N];
int check(int dl){
    if (b[m]<=dl){
        return 0;
    }
    int pos=std::upper_bound(b+1,b+1+m,dl)-b;
    int sum1=dl*(pos-1)-tol[pos-1],sum2=(tol[m]-tol[pos-1])-(m-pos+1)*dl;
    if (B<=A){
        return sum2*B;
    }else{
        if (sum1>=sum2){
            return sum2*A;
        }else{
            return sum1*A+(sum2-sum1)*B;
        }
    }
}
void yrzr(){
    std::cin>>A>>B>>C>>n>>m;
    for (int i=1;i<=n;i++){
        std::cin>>t[i];
    }
    for (int i=1;i<=m;i++){
        std::cin>>b[i];
    }
    std::sort(t+1,t+1+n);
    for (int i=1;i<=n;i++){
        sum[i]=sum[i-1]+t[i];
    }
    std::sort(b+1,b+1+m);
    for (int i=1;i<=m;i++){
        tol[i]=tol[i-1]+b[i];
    }
    if (C==1e16){
        std::cout<<check(t[1]);
        return;
    }

    int ans=1e18;
    int l=0,r=100000,res1,res2;
    while (l<r){
        int mid1=(2*l+r)/3,mid2=(l+2*r)/3;
        int pos1=std::upper_bound(t+1,t+1+n,mid1)-t-1;
        int pos2=std::upper_bound(t+1,t+1+n,mid2)-t-1;
        res1=(mid1*pos1-sum[pos1])*C+check(mid1);
        res2=(mid2*pos2-sum[pos2])*C+check(mid2);
        if (res1>res2){
            l=mid1+1;
        }else{
            r=mid2-1;
        }
        ans=std::min(ans,std::min(res1,res2));
        // std::cout<<mid1<<" "<<res1<<" "<<mid2<<" "<<res2<<"\n";
    }
    std::cout<<ans;
}

分数规划

小咪买东西
板子

void yrzr(){
    int n,k;
    std::cin>>n>>k;
    std::vector<int> c(n+1),v(n+1);
    for (int i=1;i<=n;i++){
        std::cin>>c[i]>>v[i];
    }
    int l=0,r=1e9,ans=0;

    auto check=[&](int x){
        std::vector<int> temp;
        for (int i=1;i<=n;i++){
            temp.push_back(v[i]-c[i]*x);
        }
        std::sort(temp.begin(),temp.end(),[&](int i,int j){
            return i>j;
        });

        int sum=0;
        for (int i=0;i<k;i++){
            sum+=temp[i];
        }
        return (sum>=0?1:0);
    };

    while (l<=r){
        int mid=(l+r)>>1;
        if (check(mid)){
            l=mid+1;
            ans=mid;
        }else{
            r=mid-1;
        }
    }
    std::cout<<ans<<"\n";
}

gpa
转换一下,其实就是买的物品个数变成了一个范围 [ n − k , n ] [n-k,n] [nk,n]
c h e c k check check的时候,一旦出现一个满足范围且 s u m > = 0 sum>=0 sum>=0的时候就是合法的

void yrzr(){
    int n,k;
    std::cin>>n>>k;
    std::vector<int> s(n+1),c(n+1);
    for (int i=1;i<=n;i++){
        std::cin>>s[i];
    }
    for (int i=1;i<=n;i++){
        std::cin>>c[i];
        c[i]*=s[i];
    }

    double l=0,r=1e9,ans=0;

    auto check=[&](double x){
        std::vector<double> temp;
        for (int i=1;i<=n;i++){
            temp.push_back(c[i]-x*s[i]);
        }
        std::sort(temp.begin(),temp.end(),[&](int i,int j){
            return i>j;
        });


        double sum=0;
        for (int i=1;i<=n;i++){
            sum+=temp[i-1];
            if (sum>0&&n-i<=k){
                return 1;
            }
        }
        return 0;
    };

    for (int i=1;i<=60;i++){
        double mid=(l+r)/2;
        if (check(mid)){
            l=mid+1;
            ans=mid;
        }else{
            r=mid-1;
        }
    }
    std::cout<<std::fixed<<std::setprecision(8)<<ans;
}

P4377 [USACO18OPEN] Talent Show G
与之前不同的是,此题没有限制选的数量,而是限制分母的和,二分完后,转换为背包问题即可

void yrzr(){
    int n,W;
    std::cin>>n>>W;
    std::vector<int> w(n+1),t(n+1);
    for (int i=1;i<=n;i++){
        std::cin>>w[i]>>t[i];
        t[i]*=1000;
    }

    int l=0,r=1e9,ans=0;

    auto check=[&](int x){
        std::vector<int> c(n+1),v(n+1);
        for (int i=1;i<=n;i++){
            c[i]=t[i]-w[i]*x;
            v[i]=w[i];
        }

        std::vector<int> f(W+1,-1e18);
        f[0]=0;
        for (int i=1;i<=n;i++){
            for (int j=W;j>=0;j--){
                f[std::min(W,j+v[i])]=std::max(f[std::min(W,j+v[i])],f[j]+c[i]);
            }
        }
        return (f[W]>=0?1:0);
    };

    while (l<=r){
        int mid=(l+r)/2;
        if (check(mid)){
            l=mid+1;
            ans=mid;
        }else{
            r=mid-1;
        }
    }
    std::cout<<ans<<"\n";
}

P4322 [JSOI2016] 最佳团体
二分答案,然后分数规划模型,转换一下发现就是重新定义每个人的点权,然后跑一个树上背包,但是树上背包要满足:儿子选了父亲一定要选
,特殊预处理一下即可

P3199 [HNOI2009] 最小圈
二分,重新定义边权,然后找负环即可
剪枝:如果松弛操作大于 11 n 11n 11n,则直接认定有负环

P3705 [SDOI2017] 新生舞会
二分后发现模型就是一个二分图最大权匹配,转换成最大费用最大流即可
d i s dis dis要设为 d o u b l e double double,两节课血的的教训

#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
constexpr int N=105;
constexpr int inf=1e15;
double eps=1e-5;
struct edge{
    int v,w;
    double cst;
    int next;
}e[4*N*N];
int n,s,t;
double ans,dis[2*N+10];
int a[N][N],b[N][N],cnt,head[2*N+10];
void ins(int u,int v,int w,double cst){
    e[++cnt]=edge{v,w,cst,head[u]};
    // std::cout<<cst<<"\n";
    head[u]=cnt;
}
int sym[2*N+5];
bool spfa(){
    std::queue<int> q;
    q.push(s);
    for (int i=1;i<=2*n+2;i++){
        dis[i]=-inf;
        sym[i]=0;
    }

    dis[s]=0;
    std::vector<int> vis(2*n+5);
    while (!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for (int i=head[u];i;i=e[i].next){
            int v=e[i].v;
            if (e[i].w>0&&dis[v]<dis[u]+e[i].cst){
                // if (v==5){
                //     std::cout<<"\n"<<u<<" "<<v<<"!!!\n";
                // }
                dis[v]=dis[u]+e[i].cst;
                if (!vis[v]){
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    // std::cout<<dis[t]<<" "<<(dis[t]>-inf)<<'\n';
    return (dis[t]>-inf);
}

int dfs(int u,int dfrom){
    // std::cout<<u<<" ";
    if (u==t){
        return dfrom;
    }
    sym[u]=1;
    int dto=0;
    for (int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if (!sym[v]&&abs(dis[v]-dis[u]-e[i].cst)<=eps&&e[i].w>0){
            // std::cerr<<u<<" "<<v<<"kkk\n";
            int res=dfs(v,std::min(e[i].w,dfrom));
            if (res>0){
                dfrom-=res;
                dto+=res;
                ans+=res*e[i].cst;
                e[i].w-=res;
                e[i^1].w+=res;
                if (dfrom==0) return dto;
            }
        }
    }
    return dto;
}
void dinic(){
    while (spfa()){
        // std::cout<<"\n\n\n";
        dfs(s,inf);
    }
}

bool check(double x){
    ans=0;
    cnt=1;
    memset(head,0,sizeof(head));
    s=2*n+1,t=2*n+2;

    for (int i=1;i<=n;i++){
        ins(s,i,1,0);
        ins(i,s,0,0);
    }
    for (int i=n+1;i<=2*n;i++){
        ins(i,t,1,0);
        ins(t,i,0,0);
    }
    for (int i=1;i<=n;i++){
        for (int j=n+1;j<=2*n;j++){
            ins(i,j,1,a[i][j-n]-x*b[i][j-n]);
            ins(j,i,0,x*b[i][j-n]-a[i][j-n]);
        }
    }
    dinic();

    return ans>=0;
}
void yrzr(){
    std::cin>>n;
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            std::cin>>a[i][j];
        }
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            std::cin>>b[i][j];
        }
    }

    double l=0,r=1e4;
    for (int i=1;i<=40;i++){
        double mid=(l+r)/2;
        // std::cout<<"\n\n\n\n"<<mid<<":\n";
        if (check(mid)){
            l=mid;
        }else{
            r=mid;
        }
    }
    std::cout<<std::fixed<<std::setprecision(6)<<r<<"\n";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值