前向看了下網絡流,再聽了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;
}