bzoj3112: [Zjoi2013]防守戰線


學習了一下費用流的做法,順便學習了一下zkw(聽說原始對偶是折中做法,這種沒什么特點的就不學了),順便研究了一下費用流的速度:(對於這題而言)upd抱歉我這個SLF優化寫反了。。。然而反過來也只是跑了2s+的點快了0.3s+,所以區別不大

解決線性規划還是單純形法優秀啊

zkw費用流適用費用值域較小,增廣路徑較短的圖(二分圖)

然后類似KM的寫法是不資瓷邊權為負的(懵逼)因為我嘗試寫了一會樣例都過不了

然而實際上構出最長路徑樹直接跑最大費用最大流就好了。。。。

 

對於這道題費用流的做法:

還是先把原始問題轉換成對偶問題(假如不轉對偶應該也是同理的推),注意此時系數和約束常數項就互換了,也就是費用和至少安插的塔可以理解為互換了

此時把約束差分。差分有一個很優美的性質,每個柿子會被加一次減一次,如果我們把每個約束看成一個點,變量相當於流到這個約束又流走

那么把式子加起來每個變量和常數和等於0,聯想到網絡流的流量平衡

我們得到如下建圖方法:

1:差分后的常數項若大於0,就從st連一條流量為該值費用無限的邊,反之從該約束連向ed連一條流量為該值相反數費用無限的邊,常數平衡

2:對於一個變量,差分后他是正值的約束連向差分后他是負值的約束,變量平衡

3:差分后相鄰的兩個約束由前一個連向后一個,流量無限費用為0:處理松弛變量的影響,和2同理

最后跑最大費用最大流

#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];

void pivot(int o,int e)
{
    c[o]/=a[o][e];
    for(int i=1;i<=m;i++)
        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++)
        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++)
            if(b[i]>d)d=b[i],e=i;
        if(d==0)return ;
        
        d=(1<<30);
        for(int i=1;i<=n;i++)
            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()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    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;
}
單純形
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf=(1<<30)-1;

struct node
{
    int x,y,c,d,next;
}a[310000];int len,last[21000];
void ins(int x,int y,int c,int d)
{
    len++;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
    a[len].next=last[x];last[x]=len;
    
    len++;
    a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;
    a[len].next=last[y];last[y]=len;
}

int st,ed;
int pre[21000],c[21000],ans,d[21000];
int list[21000];bool v[21000];
bool spfa()
{
    memset(d,63,sizeof(d));d[st]=0;c[st]=(1<<30);
    memset(v,false,sizeof(v));v[st]=true;
    int head=1,tail=2;list[1]=st;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[k].c>0&&d[y]>d[x]+a[k].d)
            {
                d[y]=d[x]+a[k].d;
                c[y]=min(a[k].c,c[x]);
                pre[y]=k;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail]=y;
                    tail++;if(tail==20100)tail=1;
                }
            }
        }
        v[x]=false;
        head++;if(head==20100)head=1;
    }
    if(d[ed]==d[0])return false;
    else
    {
        int y=ed;ans+=c[ed]*d[ed];
        while(y!=st)
        {
            int k=pre[y];
            a[k].c-=c[ed];
            a[k^1].c+=c[ed];
            y=a[k].x;
        }
        return true;
    }
}

int p[1100];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m); st=n+2,ed=n+3;
    len=1;
    for(int i=1;i<=n;i++)
        scanf("%d",&p[i]),ins(i,i+1,inf,0);
    
    for(int i=1;i<=n+1;i++)
        if(p[i]-p[i-1]>0)ins(st,i,p[i]-p[i-1],0);
        else              ins(i,ed,p[i-1]-p[i],0);
        
    int l,r,d;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&l,&r,&d);
        ins(l,r+1,inf,-d);
    }
    
    while(spfa());
    printf("%d\n",-ans);
    
    return 0;
}
MCMF
zkw(SLF)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf=(1<<30)-1;

struct node
{
    int x,y,c,d,next;
}a[210000];int len,last[1100];
void ins(int x,int y,int c,int d)
{
    len++;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
    a[len].next=last[x];last[x]=len;
    
    len++;
    a[len].y=x;a[len].x=y;a[len].c=0;a[len].d=-d;
    a[len].next=last[y];last[y]=len;
}

int st,ed;
int head,tail,list[1100]; bool v[1100];
int d[1100];
void spfa()
{
    memset(v,false,sizeof(v));v[ed]=true;
    memset(d,-63,sizeof(d));d[ed]=0;
    head=1,tail=2;list[head]=ed;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[k^1].c>0&&d[y]<d[x]+a[k^1].d)
            {
                d[y]=d[x]+a[k^1].d;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail]=y;
                    tail++;if(tail==1050)tail=1;
                }
            }
        }
        v[x]=false;
        head++;if(head==1050)head=1;
    }
}
int ans;
int findflow(int x,int f)
{
    if(x==ed){ans+=d[st]*f;return f;}
    int s=0;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(v[y]==false&&d[x]-a[k].d==d[y]&&a[k].c>0&&s<f)
        {
            v[y]=true;
            int t=findflow(y,min(a[k].c,f-s));
            s+=t;a[k].c-=t;a[k^1].c+=t;
        }
    }
    return s;
}
bool adjust()
{
    int tmp=-inf;
    for(int x=1;x<=ed;x++)
        if(v[x]==true)
        {
            for(int k=last[x];k;k=a[k].next)
            {
                int y=a[k].y;
                if(v[y]==false&&a[k].c>0)
                    tmp=max(tmp,d[y]+a[k].d-d[x]);
            }
        }
    if(tmp==-inf)return false;
    for(int i=1;i<=ed;i++)
        if(v[i]==true)d[i]+=tmp;
    return true;
}

int c[1100];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m); st=n+2,ed=n+3;
    len=1;
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]),ins(i,i+1,inf,0);
    
    for(int i=1;i<=n+1;i++)
        if(c[i]-c[i-1]>0)ins(st,i,c[i]-c[i-1],0);
        else              ins(i,ed,c[i-1]-c[i],0);
        
    int l,r,d;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&l,&r,&d);
        ins(l,r+1,inf,d);
    }
    
    ans=0;
    spfa();
    do
    {
        do 
        {
            memset(v,false,sizeof(v));v[st]=true;
        }while(findflow(st,inf));
    }while(adjust());
    printf("%d\n",ans);
    
    return 0;
}
zkw(KM)

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: