網絡流與線性規劃24題02太空飛行計劃問題

問題描述:
W 教授正在爲國家航天中心計劃一系列的太空飛行。每次太空飛行可進行一系列商業
性實驗而獲取利潤。現已確定了一個可供選擇的實驗集合E={E1,E2,…,Em},和進行這
些實驗需要使用的全部儀器的集合I={I1,I2,…In}。實驗Ej需要用到的儀器是I的子集RjÍI。
配置儀器Ik的費用爲ck美元。實驗Ej的贊助商已同意爲該實驗結果支付pj美元。W教授的
任務是找出一個有效算法,確定在一次太空飛行中要進行哪些實驗並因此而配置哪些儀器才
能使太空飛行的淨收益最大。這裏淨收益是指進行實驗所獲得的全部收入與配置儀器的全部
費用的差額。
編程任務:
對於給定的實驗和儀器配置情況,編程找出淨收益最大的試驗計劃。
數據輸入:
由文件input.txt提供輸入數據。文件第1行有2 個正整數m和n。m是實驗數,n是儀
器數。接下來的m 行,每行是一個實驗的有關數據。第一個數贊助商同意支付該實驗的費
用;接着是該實驗需要用到的若干儀器的編號。最後一行的n個數是配置每個儀器的費用。
結果輸出:
程序運行結束時,將最佳實驗方案輸出到文件output.txt 中。第1 行是實驗編號;第2
行是儀器編號;最後一行是淨收益。

輸入文件示例:

2 3
10 1 2
25 2 3
5 6 7

輸出文件示例:

1 2
1 2 3
17

分析:

剛拿到這題很迷茫,上網搜了相關的資料,說是最大閉合權圖的問題,然後去看了胡伯濤《最小割模型在信息學競賽中的應用》,然後又去下載了相關的最小割與最大流的相關理論,最後終於明白了這道題目。這題是最大權閉合圖問題,可以轉化成最小割問題,可用最大流解決。

把每個儀器看作X頂點,把實驗看作Y頂點,增加S和T點。

S點連接所有的X頂點,權值爲每個儀器的費用,T點連接所有實驗,權值是收入。

如果實驗需要相應的儀器,則把對應的X,Y頂點相連,權值爲無窮大,

那麼最大收入則爲所有實驗的收入減去最小割,根據最小割最大流定理,最小割就是最大流。

對應的解就是最小割劃分出的T集合中的點,也就是最後一次尋找增廣路徑時不能從S訪問到的頂點。

然後,上網又看了一下Edmonds-Karp算法,發現網絡上有個版本比我現在用着的快一點,我用的版本是求出所有的增廣路,最後檢查T的可行性,而那個版本是隻要增廣路到了T點,就立刻更新流。覺得很好用,就把寫好的算法又改了一下。具體區別可對比我的上一題 01飛行員配對方案問題。

代碼:

#include<cstdio>
#include<sstream>
#include<iostream>
#include<queue>
#include<string>
#include<cstring>
using namespace std;
const int INF = 100000000;
int main()
{
    int cap[101][101];
    int flow[101][101];
    int n,m,f=0;
    scanf("%d%d",&m,&n);
    getchar();
    char temp[100];
    int cost,num,sum=0;
    int s=0,t=n+m+1;
    //建圖
    for(int i=1;i<=m;i++){
        cin.getline(temp,100);
        stringstream in(temp);//string流
        in>>cost;
        sum+=cost;
        cap[n+i][n+m+1]=cost;
        while(in>>num) cap[num][n+i]=INF;
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&cost);
        cap[0][i]=cost;
    }
    //Edmonds-Karp算法求最大流
    queue<int> q;
    int a[101];
    int p[101];
    memset(a,0,sizeof(a));
    q.push(s);
    a[0]=INF;
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int i=1;i<=t;i++) if(!a[i]&&cap[now][i]>flow[now][i]){
            q.push(i);
            p[i]=now;
            a[i]=min(a[now],cap[now][i]-flow[now][i]);
            if(i==t){
                for(int i=t;i!=s;i=p[i]){
                    flow[p[i]][i]+=a[t];
                    flow[i][p[i]]-=a[t];
                }
                f+=a[t];
                memset(a,0,sizeof(a));
                while(!q.empty()) q.pop();
                q.push(s);
                a[0]=INF;
                break;
            }
        }
    }
    //輸出解
    for(int i=n+1;i<t;i++)
        if(!a[i]) printf("%d ",i-n);
    printf("\n");
    for(int i=1;i<=n;i++)
        if(!a[i]) printf("%d ",i);
    printf("\n");
    printf("%d\n",sum-f);
    return 0;
}

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