bzoj3550: [ONTAK2010]Vacation&&bzoj3112: [Zjoi2013]防守戰線
阿新 • • 發佈:2018-12-31
學了下單純形法解線性規劃
看起來好像並不是特別難,第二個code有註釋。我還有...*=-....這個不是特別懂
第一個是正常的,第二個是解對偶問題的
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; const double eps=1e-7; int n,m; double sum;double a[1100][1100],b[1100],c[1100]; void pivot(int o,int e) { b[o]/=a[o][e]; for(int i=1;i<=n;i++) if(i!=e)a[o][i]/=a[o][e]; a[o][e]=1/a[o][e]; for(int i=1;i<=m;i++) if(i!=o&&fabs(a[i][e])>eps) { b[i]-=b[o]*a[i][e];for(int j=1;j<=n;j++) if(j!=e)a[i][j]-=a[o][j]*a[i][e]; a[i][e]*=-a[o][e]; } sum+=c[e]*b[o]; for(int i=1;i<=n;i++) if(i!=e)c[i]-=a[o][i]*c[e]; c[e]*=-a[o][e]; } void simplex() { int e,o; double d; while(1) { d=0; for(int i=1;i<=n;i++) if(c[i]>d)d=c[i],e=i; if(d==0)return ; d=(1<<30); for(int i=1;i<=m;i++) if(a[i][e]>eps&&d>b[i]/a[i][e]) d=b[i]/a[i][e],o=i; if(d==(1<<30)){sum=(1<<30);return ;} pivot(o,e); } } int main() { int K; scanf("%d%d",&n,&K);m=2*n+1;n*=3; for(int i=1;i<=n;i++)scanf("%lf",&c[i]); for(int i=1;i<=m;i++) { b[i]=K; for(int j=1;j<=n/3;j++)a[i][i+j-1]++; } for(int i=1;i<=n;i++) b[m+i]=1,a[m+i][i]++; m+=n; sum=0;simplex(); printf("%.0lf\n",sum); return 0; }
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; const double eps=1e-7; int n,m; double sum; double a[1100][11000],b[11000],c[1100]; //解的過程中,由於基變數xi=ci,所以c也代表放在當前約束的基變數取值 void pivot(int o,int e)//把作為第o個約束的基變數用e替換 { c[o]/=a[o][e];//沒有替換之前,e的取值被當前限制,是c[o]/a[i][e],先令x=c for(int i=1;i<=m;i++)//把e的係數消掉,其實常數項c也是同理的 if(i!=e)a[o][i]/=a[o][e]; a[o][e]=1/a[o][e];//難點!取倒數相當於保留了自己的常數項,而除以了上一個的常數項,這樣下面就可以直接消除上一個的影響了 for(int i=1;i<=n;i++) if(i!=o&&fabs(a[i][e])>eps)//對於其它的約束條件,把離基的變數用進基的變數替代 { c[i]-=c[o]*a[i][e]; for(int j=1;j<=m;j++) if(j!=e)a[i][j]-=a[o][j]*a[i][e]; a[i][e]*=-a[o][e]; } sum+=b[e]*c[o];//係數乘以值 for(int i=1;i<=m;i++)//對於第i個變數當前已經用了b[e]*a[o][i]來貢獻答案了 if(i!=e)b[i]-=a[o][i]*b[e]; b[e]*=-a[o][e]; } void simplex() { int e,o; double d; while(1) { d=0;//找進基的變數 for(int i=1;i<=m;i++)//基變數的b一定<=0,在非基變數中找一個對答案貢獻最大(係數最大)的進基 if(b[i]>d)d=b[i],e=i; if(d==0)return ; d=(1<<30);//找離基的變數,進基變數係數的非負比要最小 for(int i=1;i<=n;i++)//找對e的最小約束(即用此新角點截距最小,也就是當前e的取值被這個約束條件約束)離基 if(a[i][e]>eps&&d>c[i]/a[i][e]) d=c[i]/a[i][e],o=i; if(d==(1<<30)){sum=(1<<30);return ;} pivot(o,e); } } int main() { int l,r; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%lf",&c[i]); memset(a,0,sizeof(a)); for(int i=1;i<=m;i++) { scanf("%d%d%lf",&l,&r,&b[i]); for(int j=l;j<=r;j++)a[j][i]++; } simplex(); printf("%.0lf\n",sum); return 0; }
---恢復內容結束---