網絡流與線性規劃24題10餐巾計劃問題

問題描述:
一個餐廳在相繼的N 天裏,每天需用的餐巾數不盡相同。假設第i天需要ri塊餐巾(i=1,
2,…,N)。餐廳可以購買新的餐巾,每塊餐巾的費用爲p分;或者把舊餐巾送到快洗部,
洗一塊需m天,其費用爲f 分;或者送到慢洗部,洗一塊需n 天(n>m),其費用爲s<f 分。
每天結束時,餐廳必須決定將多少塊髒的餐巾送到快洗部,多少塊餐巾送到慢洗部,以及多
少塊保存起來延期送洗。但是每天洗好的餐巾和購買的新餐巾數之和,要滿足當天的需求量。
試設計一個算法爲餐廳合理地安排好N 天中餐巾使用計劃,使總的花費最小。
編程任務:
編程找出一個最佳餐巾使用計劃.
數據輸入:
由文件input.txt提供輸入數據。文件第1 行有6 個正整數N,p,m,f,n,s。N 是要安排餐巾
使用計劃的天數;p 是每塊新餐巾的費用;m 是快洗部洗一塊餐巾需用天數;f 是快洗部洗
一塊餐巾需要的費用;n是慢洗部洗一塊餐巾需用天數;s是慢洗部洗一塊餐巾需要的費用。
接下來的N 行是餐廳在相繼的N 天裏,每天需用的餐巾數。
結果輸出:
程序運行結束時,將餐廳在相繼的N 天裏使用餐巾的最小總花費輸出到文件output.txt
中。
輸入示例        輸出示例

3 10 2 3 3 2   145
5
6
7

分析:

很明顯的一個網絡優化問題,最小費用最大流。約束條件是每天要有足夠的乾淨的餐巾,可能是快洗的,慢洗的,或者是全新的。每天的餐巾分成兩個頂點,一個是全新的要用的X,一個是用完的Y。建立源點匯點,S,T。S連接所有的Y點,容量爲每天的需求量,費用0。所有Y點連接T,容量也爲每天的需求,費用爲0。然後爲Y的來源連接邊。所有X點向Y+m連接,容量無窮,費用爲f,所有X點向Y+n點連接,容量爲無窮,費用爲s。這裏要說的是容量是當天所用的量或者無窮都沒關係,因爲S與該點的容量限制了數目,所以這裏的連接不影響結果,只要足夠大就行。然後把所有的Y和Y+1點連接,表明乾淨的多餘的可以留着明天用。然後把S和第一個Y點連接費用爲p,容量爲無窮。這樣就OK了。

值得一提的我這種建模方法是不連接X和X+1點的,換句話說要麼連接X和X+1,要麼連接Y和Y+1。連接X和X+1表明髒的餐巾可以留到第二天,然後再考慮洗不洗。而Y和Y+1表明多餘的乾淨的餐巾可以留到第二天再考慮分配,這樣費用爲P的邊需要一條就足夠了,第一天買足量的新餐巾可以往後滯留。而如果是X和X+1的連法,則需要對每個Y點都需要與S連接一條容量無窮費用爲p的邊。

我的代碼用的是我的解法,另一種解法就是連邊不同而已,但是最後的答案是一樣的,可以自行百度。如果看不懂分析的同學可以用紙和筆自己畫一畫就明白了。

最後要說的是這不是二分圖,因爲Y之間或者X之間是相連的。

代碼:

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 808*2;
const int INF = 1<<30;
int S,T;
int d[maxn],p[maxn],a[maxn];
bool inqueue[maxn];
struct edge{
    int from,to,cap,flow,cost;
    edge(int a,int b,int c,int d,int f):from(a),to(b),cap(c),flow(d),cost(f){}
};

vector<edge> edges;
vector<int> g[maxn];

void addedge(int from,int to,int cap,int cost)
{
    edges.push_back(edge(from,to,cap,0,cost));
    edges.push_back(edge(to,from,0,0,-cost));
    int m=edges.size();
    g[from].push_back(m-2);
    g[to].push_back(m-1);
}
bool bellman_spfa(int &flow,int &cost)
{
    for(int i=0;i<=T;i++) d[i]=INF;
    memset(inqueue,false,sizeof(inqueue));
    d[S]=0;inqueue[S]=true; p[S]=0;a[S]=INF;
    queue<int >q;
    q.push(S);

    while(!q.empty()){
        int x=q.front();q.pop();inqueue[x]=false;
        for(int i=0;i<g[x].size();i++){
            edge &e =edges[g[x][i]];
            if(e.cap>e.flow&&d[e.to]>d[x]+e.cost){
                d[e.to]=d[x]+e.cost;
                p[e.to]=g[x][i];
                a[e.to]=min(a[x],e.cap-e.flow);
                if(!inqueue[e.to]){ inqueue[e.to]=true;q.push(e.to); }
            }
        }
    }
    if(d[T]==INF) return false;
    flow+=a[T];
    cost+=d[T]*a[T];
    int x=T;
    while(x!=S){
        edges[p[x]].flow+=a[T];
        edges[p[x]^1].flow-=a[T];
        x=edges[p[x]].from;
    }
    return true;
}
int mincost()
{
    int flow=0,cost=0;
    while(bellman_spfa(flow,cost));
    return cost;
}
int main()
{
    int N,p,m,f,n,s,need;
    scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s);
    S=2*N;T=S+1;
    for(int i=0;i<N;i++){
        scanf("%d",&need);
        if(i==0) addedge(S,i,INF,p);//買新餐巾的邊
        if(i+m<N) addedge(i+N,i+m,INF,f);//舊的送快洗
        if(i+n<N) addedge(i+N,i+n,INF,s);//舊的送慢洗
        if(i+1<N) addedge(i,i+1,INF,0);//新的可以留着第二天用
        addedge(i,T,need,0);//容量限制爲每天的需求
        addedge(S,i+N,need,0);//同上
    }
    printf("%d\n",mincost());
    return 0;
}

發佈了34 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章