线性规划
单纯形
求解线性规划最优解问题(全部转化成约束条件<=(非负条件可以是>=),并且求最大值问题)
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()); }
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()); }
拉格朗日乘数法
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); }