线性规划

单纯形

求解线性规划最优解问题(全部转化成约束条件<=(非负条件可以是>=),并且求最大值问题)

 

bzoj1061 志愿者招募

题目大意:给定n天,每天需要志愿者ai人;m类志愿者,从si~ti天工作,每人要ci元。求最小费用。

思路:根据题意可以列出目标函数是min sigma(i=1~m)cixi,约束条件是 sigma(i=1~m)Aijxi>=aj(j=1~n)(Aij表示这一天志愿者能否工作),xi>=0。这个问题如果*-1转化的话,可能要求解辅助约束。所以可以用对偶问题来解决,目标函数是max sigma(i=1~n)aiyi,约束条件是sigma(i=1~n)Aijyi<=cj(j=1~m),yi>=0,然后就可以求解了。pivot是转轴过程,相当于把xl约束中的xe提到左边,同时代入其他的约束和目标函数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
#define M 10005
#define LD double
#define eps 1e-9
#define inf 1e10
using namespace std;
LD a[M][N],b[M],c[M];
int n,m;LD v;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
void pivot(int l,int e){
    int i,j;
    b[l]/=a[l][e];
    for (i=1;i<=n;++i)
      if (i!=e) a[l][i]/=a[l][e];
    a[l][e]=1./a[l][e];
    for (i=1;i<=m;++i)
        if (i!=l&&cmp(a[i][e],eps)!=0){
            b[i]-=a[i][e]*b[l];
            for (j=1;j<=n;++j)
                if (j!=e) a[i][j]-=a[i][e]*a[l][j];
            a[i][e]=-a[i][e]*a[l][e];}
    v+=c[e]*b[l];
    for (i=1;i<=n;++i)
      if (i!=e) c[i]-=a[l][i]*c[e];
    c[e]=-a[l][e]*c[e];}
LD simplex(){
    int i,j,l,e;LD del;
    while(true){
        for (i=1;i<=n;++i) if (c[i]>eps) break;
        if (i>n) return v;
        del=inf;e=i;
        for (i=1;i<=m;++i)
            if (a[i][e]>eps&&del>b[i]/a[i][e]){
                del=b[i]/a[i][e];l=i;
            }
        if (cmp(del,inf)==0) return inf;
        else pivot(l,e);
    }}
int main(){
    int i,j,s,t,cc;scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i) scanf("%lf",&c[i]);
    for (i=1;i<=m;++i){
        scanf("%d%d%lf",&s,&t,&b[i]);
        for (j=s;j<=t;++j) a[i][j]=1.;
    }printf("%.0f\n",simplex());
}
View Code

 

bzoj3112 防守战线

题目大意:给定n个位置和每个位置建一个防具的花费,每个位置可以建无穷多个防具,m个要求(s~t位置的防具个数不少于d个),求最小费用。

思路:和上面的一样,只是b、c、n、m互换了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
#define M 10005
#define inf 1e10
#define eps 1e-9
#define LD double
using namespace std;
LD a[N][M]={0.},b[N],c[M],v=0.;
int n,m;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
void pivot(int l,int e){
    int i,j;
    b[l]/=a[l][e];
    for (i=1;i<=m;++i)
      if (i!=e) a[l][i]/=a[l][e];
    a[l][e]=1./a[l][e];
    for (i=1;i<=n;++i)
        if (i!=l&&cmp(a[i][e],0.)!=0){
            b[i]-=b[l]*a[i][e];
            for (j=1;j<=m;++j)
                if (j!=e) a[i][j]-=a[l][j]*a[i][e];
            a[i][e]=-a[i][e]*a[l][e];}
    v+=c[e]*b[l];
    for (i=1;i<=m;++i)
      if (i!=e) c[i]-=c[e]*a[l][i];
    c[e]=-c[e]*a[l][e];}
LD simplex(){
    int l,e,i,j;LD del;
    while(true){
        for (i=1;i<=m;++i) if (c[i]>eps) break;
        if ((e=i)>m) return v;
        del=inf;
        for (i=1;i<=n;++i)
            if (a[i][e]>eps&&cmp(del,b[i]/a[i][e])>0){
                del=b[i]/a[i][e];l=i;}
        if (cmp(del,inf)==0) return inf;
        else pivot(l,e);
    }}
int main(){
    int i,j,s,t;scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i) scanf("%lf",&b[i]);
    for (i=1;i<=m;++i){
        scanf("%d%d%lf",&s,&t,&c[i]);
        for (j=s;j<=t;++j) a[j][i]=1.;
    }printf("%.0f\n",simplex());
}
View Code

 

拉格朗日乘数法

bzoj2876 骑行川藏

题目大意:已知n段路程的距离si、参数ki、风速vi和初始能量Eu,给n段路程定一个速度,每一段的能量为Ei=ki*(vi-vi')^2*si,要求总能量<=Eu,求最短时间。

思路:相当于sigma(i=1~n)Ei<=Eu,min(si/xi)。对于这样的约束系统,可以用拉格朗日乘数法,得到偏导数方程组2*λ*ki*vi^2*(vi-vi')=1(vi>=vi'),其中E随vi递增,E随λ递减,可以二分λ,找到sigma(i=1~n)Ei=Eu的λ,通过二分解出每个vi,这道题目中因为只有一个λ,所以可以算出唯一解。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1e9
#define eps 1e-14
#define LD double
#define N 10005
using namespace std;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
int n;
LD eu,si[N],ki[N],vv[N],vi[N];
LD getf(LD x,int i){return x*x*(x-vv[i]);}
LD sqr(LD x){return x*x;}
LD judge(LD x){
    int i;LD aa,l,r,mid,sm=0.;
    for (i=1;i<=n;++i){
        aa=1./2./x/ki[i];
        l=max(0.,vv[i]);r=inf;
        while(r-l>eps){
            mid=(l+r)/2.;
            if (cmp(getf(mid,i),aa)>=0) r=mid;
            else l=mid;
        }vi[i]=l;
        sm+=ki[i]*sqr(vi[i]-vv[i])*si[i];
    }return sm;}
int main(){
    int i;LD l,r,mid,ans=0.;scanf("%d%lf",&n,&eu);
    for (i=1;i<=n;++i) scanf("%lf%lf%lf",&si[i],&ki[i],&vv[i]);
    l=0.;r=inf;
    while(r-l>eps){
        mid=(l+r)/2.;
        if (cmp(judge(mid),eu)>=0) l=mid;
        else r=mid;
    }for (i=1;i<=n;++i) ans+=si[i]/vi[i];
    printf("%.8f\n",ans);
}
View Code

 

posted @ 2016-01-10 20:29  Rivendell  阅读(389)  评论(0编辑  收藏  举报