bzoj 1061: [Noi2008]志願者招募(線性規劃+網絡流)

Description

申奧成功後,布布經過不懈努力,終於成爲奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個難題:爲即將啓動的奧運新項目招募一批短期志願者。經過估算,這個項目需要N 天才能完成,其中第i 天至少需要Ai 個人。 布布通過了解得知,一共有M 類志願者可以招募。其中第i 類可以從第Si 天工作到第Ti 天,招募費用是每人Ci 元。新官上任三把火,爲了出色地完成自己的工作,布布希望用盡量少的費用招募足夠的志願者,但這並不是他的特長!於是布布找到了你,希望你幫他設計一種最優的招募方案。

Input

第一行包含兩個整數N, M,表示完成項目的天數和可以招募的志願者的種類。 接下來的一行中包含N 個非負整數,表示每天至少需要的志願者人數。 接下來的M 行中每行包含三個整數Si, Ti, Ci,含義如上文所述。爲了方便起見,我們可以認爲每類志願者的數量都是無限多的。

Output

僅包含一個整數,表示你所設計的最優方案的總費用。

本題可以說是最經典的一個線性規劃與網絡流結合的題了,對於這個題,,我當然是不會的,上網看了題解,,才發現有這麼神的構圖方法。下面說說我的一點理解:
首先我們先要明白網絡流的一個性質:除了源點和匯點外,每個點的入流和出流是相等的。那麼我們就可以根據這個性質來建圖;首先我們先要將題目轉化爲一個數學模型:將其轉化爲一系列的等式或不等式,如果是不等式的話可以添加變量將其變爲等式,這樣我們就可以將每個等式看作一個點,那麼等式的左右兩端就分別是該點的入流和出流,,注意我們列的等式要滿足幾個要求:首先儘量將常量放在等式的一邊,變量放在另一邊;其次對於每個我們不知道的變量要做到該變量在所有等式中的權值和爲0(即所有的等式帶有變量的一邊之和=0),所有等式的常量部分相加也應爲0,這是爲了保證整張圖的入流和出流相等;
我講的也不是特別好,建圖部分和本題具體的詳解可以參見另一篇大神的博文
https://www.byvoid.com/blog/noi-2008-employee/#more-916

下面是代碼:

#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#include<cstdio>
#include<climits>
#define N 1020
#define INF INT_MAX/3
using namespace std;
struct Edge { 
    int fr,to,cap,cost;
    Edge(int f,int t,int ca,int co) { 
      fr=f;to=t;cap=ca;cost=co; 
    }
};
vector<int> g[N+10];vector<Edge> edge;
int n,m,ans,S=0,T=N,p[N+10],dis[N+10],ca[N+10],v[N+10];
void Add_edge(int a,int b,int cat,int cost) {
    edge.push_back(Edge(a,b,cat,cost));edge.push_back(Edge(b,a,0,-cost));
    int t=edge.size();g[a].push_back(t-2),g[b].push_back(t-1); return;
}
int spfa() {
    memset(dis,127/3,sizeof(dis));p[T]=-1;p[S]=-1;
    queue<int> q;q.push(S);ca[S]=INF;dis[S]=0;v[S]=true;
    while(!q.empty()) {
      int x=q.front(),l=g[x].size();q.pop();v[x]=false;
      for(int i=0;i<l;i++) {
        int ti=g[x][i],to=edge[ti].to;Edge &e=edge[ti];
        if(e.cap>0&&dis[to]>dis[x]+e.cost) {
          dis[to]=dis[x]+e.cost; p[to]=ti; ca[to]=min(e.cap,ca[x]);
          if(!v[to]) v[to]=true,q.push(to);
        }
      }
    }
    if(p[T]==-1) return 0;ans+=ca[T]*dis[T];
    for(int i=p[T];i!=-1;i=p[edge[i].fr]) {
      edge[i].cap-=ca[T];edge[i^1].cap+=ca[T];
    } return 1;
}
void min_cost() {
    while(spfa()); printf("%d",ans);return;
}
int in() {
    int s=0;char c=getchar(); while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar();return s;
}
int main() {
    int t=0,s=0;n=in(),m=in(); for(int i=1;i<=n;i++) {
      t=s;s=in();t=s-t;
      if(t==0) goto end;
      if(t>0) Add_edge(S,i,t,0); else Add_edge(i,T,t*(-1),0); 
      end: Add_edge(i+1,i,INF,0);
    } Add_edge(n+1,T,s,0);
    int c,l,r;for(int i=1;i<=m;i++) {
      l=in();r=in();c=in(); Add_edge(l,r+1,INF,c);
    } min_cost(); return 0;
}

PS: bzoj上面還有很多跑的很快的大神們,,據說是因爲有特殊的建圖方法。有興趣的自己去找吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章