01分数规划

https://blog.csdn.net/hzoi_ztx/article/details/54898323

01分数规划问题主要包含以下几个问题:

  • 一般的01分数规划
  • 最优比率生成树
  • 最优比率环

 

关于二分判断条件的理解。

给出几道例题:一、POJ-2976

入门

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cctype>
#include <cmath>
#include <time.h>
#include <map>
#include <set>
#include <vector>

using namespace std;

#define ms(a,b) memset(a,b,sizeof(a))

typedef long long ll;

const double eps=1e-7;

int n,k;
double a[1010],b[1010],d[1010];

inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

int main()
{
    while (1)
    {
        n=read(),k=read();
        if (n==0 && k==0) break;
        for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
        for (int i=1;i<=n;i++) scanf("%lf",&b[i]);
        double l=0.0,r=1.0,mid;
        while (r-l>eps)
        {
            mid=(l+r)/2;
            for (int i=1;i<=n;i++) d[i]=a[i]-mid*b[i];
            sort(d+1,d+1+n);
            double sum=0.0;
            for (int i=k+1;i<=n;i++) sum+=d[i];
            if (sum>0) l=mid;
            else r=mid;
        }
        printf("%.0f\n",mid*100);
    }
    return 0;
}
View Code

51nod

也是入门

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cctype>
#include <cmath>
#include <time.h>
#include <map>
#include <set>
#include <vector>

using namespace std;

#define ms(a,b) memset(a,b,sizeof(a))

typedef long long ll;

const int maxn = 50005;

const double eps = 1e-9;

ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}

struct node
{
    int w,p;
    double val;
    bool operator < (const node& T) const{
        return val>T.val;
    }
}b[maxn];

double mid;
ll anss,ansx,tmps,tmpx;
int n,k;

bool check()
{
    int tot=0;
    for(int i=0;i<n;i++)b[i].val = 1.0*b[i].p-b[i].w*mid;
    sort(b,b+n);
    double sum=0;
    tmps=0,tmpx=0;
    for(int i=0;i<k;i++)sum+=b[i].val,tmps+=b[i].p,tmpx+=b[i].w; 
    if(sum-0>=eps)return true;
    return false;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++) scanf("%d%d",&b[i].w,&b[i].p);
    double l=0,r=500000;
    for(int i=0;i<100;i++)
    {
        mid=(l+r)/2;
        if(check())l=mid,anss=tmps,ansx=tmpx;
        else r=mid;
    }
    ll tmp=gcd(anss,ansx);
    anss/=tmp,ansx/=tmp;
    printf("%lld/%lld\n",anss,ansx);
    return 0;
}
View Code

POJ - 2728(最优比率生成树)

大意:给定一张图,每条边有一个收益值和一个花费值, 求一个生成树,要求花费/收益最小,输出这个值
分析:现在的限制就有点复杂了,要求解必须是一棵生成 树。而且这道题目要求的花费/收益最小,当然你求收益/ 花费最大然后反过来也是可以的,注意处理花费为0的情 况。如果求最小的,处理方法是也类似的,先求个D,然 后做一次最小生成树,显然得到的就是函数值。

POJ-3621(最优比率环)

大意:给定一张图,边上有花费,点上有收益,点可以多 次经过,但是收益不叠加,边也可以多次经过,但是费用 叠加。求一个环使得收益和/花费和最大,输出这个比值。
分析:比上面更加的恶心了。先不说环的问题,就是花费 和收益不在一处也令人蛋疼。这时候需要用到几个转化和 结论。
首先的一个结论就是,不会存在环套环的问题,即最优的方 案一定是一个单独的环,而不是大环套着小环的形式。这个的 证明其实非常的简单,大家可以自己想一下(提示,将大环上 的收益和记为x1,花费为y1,小环上的为x2,y2。重叠部分的花 费为S。表示出来分类讨论即可)。有了这个结论,我们就可以 将花费和收益都转移到边上来了,因为答案最终一定是一个环, 所以我们将每一条边的收益规定为其终点的收益,这样一个环 上所有的花费和收益都能够被正确的统计。
解决了蛋疼的问题之后,就是01分数规划的部分了,我们只 需要计算出D数组后找找有没有正权环即可,不过这样不太好, 不是我们熟悉的问题,将D数组全部取反之后,问题转换为查找 有没有负权环,用spfa或是bellman_ford都可以。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 100010
using namespace std;
const double eps=1e-5;
struct edge
{
    int v,nxt,w;
    double c;
} e[N<<1];
int head[N],f[N];
bool vis[N];
double dis[N];
int n,m,mct,u,v,w;
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void add(int u,int v,int w)
{
    e[++mct].v=v;e[mct].nxt=head[u];e[mct].w=w;head[u]=mct;
}
bool spfa(int u)
{
    vis[u]=1;
    for(int i=head[u]; i; i=e[i].nxt)
    {
        int v=e[i].v;
        if(dis[v]>dis[u]+e[i].c)
        {
            dis[v]=dis[u]+e[i].c;
            if(vis[v] || spfa(v))
            {
                vis[v]=0;
                return 1;
            }
        }
    }vis[u]=0;return 0;
}
void judge(double r)
{
    for(int i=1; i<=mct; i++)
        e[i].c=(double)e[i].w*r-f[e[i].v];
    return;
}
bool check()
{
    for(int i=1; i<=n; i++)
        if(spfa(i))return 1;
    return 0;
}
int main()
{
    n=read();m=read();
    for(int i=1; i<=n; i++) f[i]=read();
    for(int i=1; i<=m; i++)
    {
        u=read();v=read();w=read();
        add(u,v,w);
    }
    double l=0,r=100000,ans;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        judge(mid);
        if(check())
        {
            ans=mid;l=mid;
        }
        else r=mid;
    }
    printf("%.2f\n",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Vikyanite/p/13668906.html