POJ2516--Minimum Cost最小費用最大流

前向看了下網絡流,再聽了vivi的講解,對最小費用最大流也算有了初步的瞭解。

可以理解爲:在一個網絡流中,每條邊都有一定的代價,可正可負可爲零。怎樣才能在耗費代價最小的情況下求出最大流?

有兩種求法。一種途徑是先用最大流算法算出最大流,然後根據邊費用,檢查是否有可能在流量平衡的前提下通過調整邊流量,使總費用得以減少?只要有這個可能,就進行這樣的調整。調整後,得到一個新的最大流。

另一種是先通過SPFA(因爲網絡中代價可負)找到最小代價路徑,這條路徑必須要滿足爲可行流,以此作爲增廣路,求出minflow,更新流量值,再進行SPFA......直至找不到增廣路爲止。則最小費用爲:∑(minflow[k]*cost[s~t]),最大流爲∑minflow。

好,我們來看POJ2516這道題:給定N個店,M個提供商和K種貨物。接下來N行爲第i個店對物品K的需求量。接下來M行爲第i個提供商對物品K的貨存量。接下來K個N*M矩陣表示第j個供應商運第K個貨物到第i個店主要花費的代價。

題目如上,要怎麼解決呢?

首先,需求量必須小於等於貨存量,否則直接"-1";再者,如果所有k個物品一起求無疑會增加負擔。但是各個物品之間的運送是不想關的,所以我們可以拆開來做。

思路理清楚了,那麼該如何建圖呢?既然有M個提供商對應N個店主,那麼就要有一個總起點和一個總彙點來約束他們,即一共要M+N+2個節點,用矩陣或鄰接表來存都可以(矩陣更直觀,鄰接表更優),由總彙點到達所有的M的流量爲M的貨存量(流經M的流量爲其可能達到的最大值),cost爲0;由所有的N到總彙點的流量爲N的需求量,cost也爲零;那麼由提供商到店主的流量即爲N的需求量,代價給定,負向弧與正向弧代價相反,流量爲0。

建圖成功了,我們就來看一下代碼吧(好像不能全過,不知道哪裏有錯,大牛們指點下呀):

#include<iostream>
#include<queue>
using namespace std;
int n,m,k,requir[51][51],provid[51][51],price[51][51],i;
int qq[110][110],cost[110][110],map[110][110],pre[110];
queue<int> myqueue;
int mim(int a,int b){
    if (a<b) return a; 
    return b;
}
int SPFA(){
    while (!myqueue.empty()) myqueue.pop();
    int j,jj,first;
    int dis[110];
    bool mark[110];
    memset(pre,0,sizeof(pre));
    memset(dis,0X7F,sizeof(dis));
    memset(mark,0,sizeof(mark));
    mark[0]=1;
    dis[0]=0;
    myqueue.push(0);
    while (!myqueue.empty()){
          first=myqueue.front();
          myqueue.pop();
          mark[first]=0;
          for (j=1;j<=m+n+1;j++)
              if (qq[first][j]&&cost[first][j]+dis[first]<dis[j]){
                 if (!mark[j]){myqueue.push(j);mark[j]=1;}
                 dis[j]=cost[first][j]+dis[first];
                 pre[j]=first;
                 }
          }
    if (dis[n+m+1]<0X7F) return 1;
    return 0;
}
int BG(){
    int sum=0,jj,j,minflow,c;
    memset(qq,0,sizeof(qq));
    memset(cost,0,sizeof(cost));
    for (j=1;j<=m;j++)
        qq[0][j]=provid[j][i];
    for (j=1;j<=n;j++)
        qq[m+j][m+n+1]=requir[j][i];
    for (j=1;j<=n;j++)
        for (jj=1;jj<=m;jj++){
            qq[jj][m+j]=provid[jj][i];
            cost[jj][m+j]=price[j][jj];
            cost[m+j][jj]=-cost[jj][m+j];
            }
    while (SPFA()){
          int p=m+n+1,prep=pre[p];
          minflow=qq[prep][p];
          for (j=m+n+1;j!=0;j=pre[j]) {
              if (j==0)break;
              minflow=mim(minflow,qq[pre[j]][j]);
              }
          sum+=cost[pre[prep]][prep]*minflow;
          while (p!=0){
                prep=pre[p];
                qq[prep][p]-=minflow;
                qq[p][prep]+=minflow;
                p=prep;
                }
          }
    return sum;
}
int main(){
    int cnt[51],sum[51],total;
    while ((cin>>n>>m>>k)&&n!=0&&m!=0&&k!=0){
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));
          bool tag=true;
          total=0;
          for (i=1;i<=n;i++)
          for (int j=1;j<=k;j++){
              cin>>requir[i][j];
              cnt[j]+=requir[i][j];
              }
          for (i=1;i<=m;i++)
          for (int j=1;j<=k;j++){
              cin>>provid[i][j];
              sum[j]+=provid[i][j];
              }
          
          for (i=1;i<=k;i++){
              if (cnt[i]>sum[i]) tag=false;
              }
          for (i=1;i<=k;i++){
              for (int j=1;j<=n;j++)
                  for (int z=1;z<=m;z++)
                      cin>>price[j][z];
              if (tag)  total+=BG(); else {total=-1;}
              }
          cout<<total<<endl;
          }
    return 0;
}


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