1. 程式人生 > >bzoj3550: [ONTAK2010]Vacation&&bzoj3112: [Zjoi2013]防守戰線

bzoj3550: [ONTAK2010]Vacation&&bzoj3112: [Zjoi2013]防守戰線

學了下單純形法解線性規劃

看起來好像並不是特別難,第二個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;
}

 

---恢復內容結束---