zoukankan      html  css  js  c++  java
  • 01分数规划

    先上基本模板后面慢慢更新

    01分数规划,简单的来说,就是有一些二元组(si,pi),从中选取一些二元组,使得∑si / ∑pi最大(最小)。

    这种题一类通用的解法就是,我们假设x = ∑si / ∑pi的最大(小)值,那么就有x * ∑pi = ∑si ,即∑s - x * ∑pi= 0。也就是说,当某一个值x满足上述式子的时候,它就是要求的值。我们可以想到枚举……不过再想想,这个可以二分答案。

    所以我们直接二分答案,当上述式子>0,说明答案小了,<0则说明答案大了,这样计算即可。

    这是一种解决问题的方法,具体应该怎么做我们要看题来分析。

    01分数规划有这样几种基本的题型(当然还有很多别的……暂时不在juruo的考虑范围内)

    1.01分数规划

    2.最优比率生成树

    3.最优比率生成环

    【更新】

    消圈定理:一个流是当前流量下的最小费用流,等价于当前残量网络上没有负费用圈。

    证明:假设残量网络上存在负费用圈,我们可以把原来不经过负圈的流沿着这个负圈增广一次,则我们的流量会不变,并且减少了所花的费用。

    #include <stdio.h>

    #include <iostream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <math.h>
    #include <string.h>
    #include <queue>
    #include <string>
    #define LL long long
    #define _LL __int64
    #define eps 1e-7
    using namespace std;
    const _LL INF = 1e18;
    const int maxn = 1010;


    int n,k;
    int a[maxn],b[maxn];
    double c[maxn];

    int main()
    {
    while(~scanf("%d %d",&n,&k) && (n || k))
    {
    for(int i = 1; i <= n; i++)
    scanf("%d",&a[i]);
    for(int i = 1; i <= n; i++)
    scanf("%d",&b[i]);

    double l = 0.0;
    double r = 1.0;
    double mid;

    while(fabs(r-l) > eps)
    {
    mid = (l+r)/2;

    for(int i = 1; i <= n; i++)
    c[i] = 1.0*a[i] - mid * 1.0 * b[i];
    sort(c+1,c+1+n);
    double sum = 0.0;
    for(int i = k+1; i <= n; i++)
    sum += c[i];
    if(sum > 0)
    l = mid;
    else if(sum < 0)
    r = mid;
    else break;
    }
    mid = mid*100;
    printf("%.0f\n",mid);
    }
    return 0;
    }

    ————————更新

    一道最小生成树上套01

    Desert King poj2728

    与生成树不一样的是生成树是按权最小来生成,我们这个得是cost/len,

    故我们可以将01规划弄出cost-r*len作为边权跑prim,结果大于小于0来二分

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <stack>
     
    using namespace std;
     
    #define For(i,j,n) for(int i=j;i<=n;i++)
    #define mst(ss,b) memset(ss,b,sizeof(ss));
     
    typedef  long long LL;
     
    template<class T> void read(T&num) {
        char CH; bool F=false;
        for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
        for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
        F && (num=-num);
    }
    int stk[70], tp;
    template<class T> inline void print(T p) {
        if(!p) { puts("0"); return; }
        while(p) stk[++ tp] = p%10, p/=10;
        while(tp) putchar(stk[tp--] + '0');
        putchar('\n');
    }
     
    const LL mod=1e9+7;
    const double PI=acos(-1.0);
    const double inf=1e18;
    const int N=15e4+10;
    const int maxn=1e3+10;
    const double eps=1e-5;
     
    int n;
    double cost[maxn][maxn],dis[maxn][maxn],x[maxn],y[maxn],z[maxn];
    int vis[maxn];
     
    double get_dis(int a,int b)
    {
        return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
    }
     
    int check(double x)
    {
        mst(vis,0);
        double sum=0,lowcost[maxn];
        vis[1]=1;
        For(i,1,n)lowcost[i]=cost[1][i]-x*dis[1][i];
        For(i,2,n)
        {
            double temp=inf;
            int k=-1;
            For(j,2,n)
            {
                if(!vis[j]&&lowcost[j]<temp)
                {
                    k=j;
                    temp=lowcost[j];
                }
            }
            if(k==-1)break;
            vis[k]=1;
            sum+=temp;
            For(j,2,n)
            {
                if(!vis[j]&&cost[k][j]-x*dis[k][j]<lowcost[j])
                lowcost[j]=cost[k][j]-x*dis[k][j];
            }
        }
        if(sum>=0)return 1;
         return 0;
    }
    int main()
    {
            while(1)
            {
                read(n);
                if(n==0)break;
                For(i,1,n)
                {
                    scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
                }
                For(i,1,n)
                For(j,i+1,n)
                {
                    dis[i][j]=dis[j][i]=get_dis(i,j);
                    cost[i][j]=cost[j][i]=abs(z[i]-z[j]);
                }
                double l=0.0,r=100.0;
                while(r-l>=eps)
                {
                    double mid=(l+r)/2;
                    if(check(mid))l=mid;
                    else r=mid;
                }
                printf("%.3f\n",r);
            }
            return 0;
    }
    

      

    Sightseeing Cows

    给定一个n(2 <= n <= 1000)个点,m(2 <= m <= 5000)条边的有向图,给定每个点的点值f(i)和每条边的权值w(i),求一个环使得路径上点权和除以边权和最大。

    简单来说就是最优比率生成环。

    题解

    01分数规划问题,按套路二分答案。

    现在问题转化为了判断可行。把边权变成midTFmid∗T−F,这样可行的话一个环的和<=0,问题转化成了判断负环。

    而如何判断负环呢?可以记录入队次数,也可以记录最短路边数。而后一种效率更高。

    #include<iostream>
    #include<cstring>
    #include<queue>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read(){
        rg T data=0,w=1;rg char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
        for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
        return data*w;
    }
    template<class T>il T read(rg T&x) {return x=read<T>();}
    typedef long long ll;
    using namespace std;
    
    co int N=1e3+1,M=5e3+1;
    co double eps=1e-6;
    int n,m,c[N],f[N],x[M],y[M],z[M];
    int Head[N],Edge[M],Next[M],tot;
    double Leng[M],d[N];
    bool v[N];
    
    il void add(int x,int y,double z){
        Edge[++tot]=y,Leng[tot]=z,Next[tot]=Head[x],Head[x]=tot;
    }
    bool judge(double w){
        tot=0,memset(Head,0,sizeof Head);
        for(int i=1;i<=m;++i) add(x[i],y[i],w*z[i]-f[x[i]]);
        queue<int> q;
        for(int i=1;i<=n;++i) q.push(i),d[i]=0,v[i]=1;
        memset(c,0,sizeof c);
        while(q.size()){
            int x=q.front();q.pop();
            v[x]=0;
            for(int i=Head[x];i;i=Next[i]){
                int y=Edge[i];
                if(d[y]>d[x]+Leng[i]){
                    d[y]=d[x]+Leng[i];
                    if((c[y]=c[x]+1)>=n) return 1;
                    if(!v[y]) q.push(y),v[y]=1;
                }
            }
        }
        return 0;
    }
    int main(){
        read(n),read(m);
        for(int i=1;i<=n;++i) read(f[i]);
        for(int i=1;i<=m;++i) read(x[i]),read(y[i]),read(z[i]);
        double l=0,r=1000;
        while(r-l>eps){
            double mid=(l+r)/2;
            if(judge(mid)) l=mid;
            else r=mid;
        }
        printf("%.2f",l);
        return 0;
    }
    

      

  • 相关阅读:
    springboot项目搭建
    linux之scp
    docker文件拷贝
    vue数据绑定不刷新可能情况
    css弹框
    jqgrid跨站脚本漏洞解决
    springboot配置文件加载顺序
    git之在eclipse上玩(一)
    windows系统日志位置
    maven
  • 原文地址:https://www.cnblogs.com/hgangang/p/11517952.html
Copyright © 2011-2022 走看看