【费用流】[BZOJ1061]/[HYSBZ1061]志愿者招募

题目
分析:建图的方法还是比较难想。
首先,计算两个相邻时刻的差分,若a[i] < a[i-1],就从i向汇点连边,容量为a[i-1]-a[i],若a[i] > a[i-1],就从源点向i连边,容量为a[i]-a[i-1]
请联系差分数组理解。
然后,对于志愿者,连s->(t+1),容量为+∞,费用为c。
最后,由于志愿者可以多不能少,连(i+1)->i,容量为+∞。
跑费用流算出费用即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define MAXN 1000
#define MAXM 10000
#define INF 0x7f7f7f7f7f7f7f7fll
using namespace std;
deque<int>q;
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
int n,m,S,T,a[MAXN+10];
typedef long long LL;
LL dist[MAXN+10],cost;
bool vis[MAXN+10];
struct node{
    int wt,v;
    LL cap;
    node *next,*back;
}*adj[MAXN+10],edge[MAXN*4+MAXM*2+10],*ecnt=edge,*pre[MAXN+10];
template<class T>
void Read(T &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void addedge(int u,int v,int wt,LL cap){
    node *p=++ecnt;
    p->v=v;
    p->wt=wt;
    p->cap=cap;
    p->next=adj[u];
    adj[u]=p;
    p=p->back=++ecnt;
    p->v=u;
    p->wt=-wt;
    p->cap=0;
    p->next=adj[v];
    adj[v]=p;
    p->back=ecnt-1;
}

void read(){
    Read(n),Read(m);
    int i,s,t,c;
    T=n+2;
    for(i=1;i<=n;i++)
        Read(a[i]);
    for(i=1;i<=n;i++){
        if(a[i]>a[i-1])
            addedge(S,i,0,a[i]-a[i-1]);
        else if(a[i]<a[i-1])
            addedge(i,T,0,a[i-1]-a[i]);
        addedge(i+1,i,0,INF);
    }
    addedge(n+1,T,0,a[n]);
    for(i=1;i<=m;i++){
        Read(s),Read(t),Read(c);
        addedge(s,t+1,c,INF);
    }
}
bool spfa(){
    memset(dist,0x7f,sizeof dist);
    memset(pre,0,sizeof pre);
    q.push_back(S);
    vis[S]=1;
    dist[S]=0;
    int u,v,len;
    LL sum;
    while(!q.empty()){
        u=q.front();
        q.pop_front();
        if(dist[u]*len>sum){
            q.push_back(u);
            continue;
        }
        len--;
        sum-=dist[u];
        vis[u]=0;
        for(node *p=adj[u];p;p=p->next){
            v=p->v;
            if(p->cap>0&&dist[v]>dist[u]+p->wt){
                dist[v]=dist[u]+p->wt;
                pre[v]=p;
                if(!vis[v]){
                    vis[v]=1;
                    if(q.empty()||dist[v]>dist[q.front()])
                        q.push_back(v);
                    else
                        q.push_front(v);
                    sum+=dist[v];
                    len++;
                }
            }
        }
    }
    if(dist[T]==INF)
        return 0;
    return 1;
}
void mcmf(){
    int u;
    LL delta;
    while(spfa()){
        delta=INF;
        for(u=T;u!=S;u=pre[u]->back->v)
            delta=min(delta,pre[u]->cap);
        for(u=T;u!=S;u=pre[u]->back->v){
            pre[u]->cap-=delta;
            pre[u]->back->cap+=delta;
            cost+=delta*pre[u]->wt;
        }
    }
}
int main()
{
    read();
    mcmf();
    printf("%lld\n",cost);
}

你可能感兴趣的:(C++,费用流)