BZOJ3876: [Ahoi2014]支線劇情 線性規劃

題意:給一個DAG,每條邊有費用,每選一次就要付出這個費用,求從1號點出發的若干條路徑將所有邊都覆蓋的最小費用。
線性規劃方程:設每條邊的選取次數爲變量。
對於每個變量:大於等於1
對於每個點:所有連向這個點的邊的經過次數之和大於等於這個點連出的所有邊經過次數之和。
最小化:sigma(每條邊經過次數*費用)
可以用對偶原理獲取初始解,但這樣矩陣是m*m的,聽說過不了。
對於每個變量x,令x’=x-1,則x’>=0,用x=x’+1替換原式中所有的x,可以將矩陣縮小到n*m,但是沒有初始解了。
線性規劃解初始解的過程:
首先對於每個小於等於的約束條件,如果我們人爲地在左邊減去一個數,當這個數很大時所有條件肯定是都能滿足的。那麼設輔助變量x0(x0>=0),在原線性規劃每個式子左邊減去x0,得到的線性規劃必定有解。
而且若x0可以取0,說明並不需要強行減去一個變量,也就是原線性規劃有解。同理,x0大於0說明原線性規劃無解。那麼我們求-x0最小值。首先找到最小的bl,執行pivot(l,0),就可以將所有的b全變成非負數,也就得到了初始解。接下來解max(-x0)。若結束後x0變爲了非基變量,由於其爲0,可以全部消去,若是基變量還需找個該行任意的非0變量執行pivot後再消去x0。然後將現在每個變量帶入原表達式,再求解最大值即可。
代碼:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cassert>
using namespace std;
const double eps=1e-8;
struct simplex
{
    int n,m;
    double a[301][5001],b[301],c[5001],v;
    int pos[5301],nex[5001];
    int B[301],N[5001];
    void pivot(int l,int e)
    {
        int fr=m+1;
        swap(pos[B[l]],pos[N[e]]);
        swap(B[l],N[e]);
        b[l]/=a[l][e];
        for(int i=0;i<=m;++i)
        if(i!=e&&fabs(a[l][i])>eps)
        {
            nex[i]=fr,fr=i;
            a[l][i]/=a[l][e];
        }
        a[l][e]=1/a[l][e];
        for(int i=1;i<=n;++i)
        if(i!=l&&fabs(a[i][e])>eps)
        {
            b[i]-=b[l]*a[i][e];
            for(int j=fr;j<=m;j=nex[j])
            {
                a[i][j]-=a[l][j]*a[i][e];
            }
            a[i][e]=-a[i][e]*a[l][e];
        }
        v+=b[l]*c[e];
        for(int i=fr;i<=m;i=nex[i])
        c[i]-=a[l][i]*c[e];
        c[e]=-c[e]*a[l][e];
    }
    void init()
    {
        for(int i=0;i<=m;++i)
        N[i]=pos[i]=i;
        for(int i=1;i<=n;++i)
        B[i]=pos[m+i]=m+i;
        double minn=1e100;
        int l=0,e;
        for(int i=1;i<=n;++i)
        if(b[i]<minn)
        minn=b[i],l=i;
        if(minn>=-eps) return;
        c[0]=-1;
        for(int i=1;i<=n;++i)
        a[i][0]=-1;
        pivot(l,0);
        double res=max();
        assert(fabs(res)<eps);
        if(pos[0]>m)
        {
            l=pos[0]-m;
            e=-1;
            for(int i=0;i<=m;++i)
            if(fabs(a[l][i])>eps)
            {
                e=i;break;
            }
            if(e==-1) return;
            pivot(l,e);
        }
        e=pos[0];
        for(int i=1;i<=n;++i)
        a[i][e]=0;
        memset(c,0,m+1<<3);
        v=0;
    }
    double max()
    {
        int l,e;
        while(1)
        {
            e=-1;
            for(int i=0;i<=m;++i)
            if(c[i]>eps)
            {
                e=i;break;
            }
            if(e==-1) return v;
            double minn=1e100;
            for(int i=1;i<=n;++i)
            if(a[i][e]>eps&&b[i]/a[i][e]<minn)
            {
                l=i,minn=b[i]/a[i][e];
            }
            pivot(l,e);
        }
    }
    void receive(double r[])
    {
        for(int i=1;i<=m;++i)
        {
            if(pos[i]<=m) c[pos[i]]+=r[i];
            else
            {
                int l=pos[i]-m;
                v+=r[i]*b[l];
                for(int j=0;j<=m;++j)
                c[j]-=r[i]*a[l][j];
            }
        }
    }
}s;
int n;
int cost[5001];
int tot;
struct e
{
    int no;
    e *n;
    e(int no,e *n):no(no),n(n){}
}*f[301],*g[301];
double a[5001];
bool final[301];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        int k,b,t;
        scanf("%d",&k);
        if(!k) final[i]=1;
        while(k--)
        {
            scanf("%d%d",&b,&t);
            cost[++tot]=t;
            f[i]=new e(tot,f[i]);
            g[b]=new e(tot,g[b]);
        }
    }
    int &cnt=s.n=0;
    s.m=tot;
    for(int i=2;i<=n;++i)
    {
        if(final[i]) continue;
        ++cnt;
        for(e *j=f[i];j;j=j->n)
        {
            ++s.a[cnt][j->no];
            --s.b[cnt];
        }
        for(e *j=g[i];j;j=j->n)
        {
            --s.a[cnt][j->no];
            ++s.b[cnt];
        }
    }
    s.init();
    for(int i=1;i<=tot;++i)
    {
        a[i]=-cost[i];
        s.v+=a[i];
    }
    s.receive(a);
    printf("%.0lf\n",-s.max());
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章