LibreOJ #6001. 「網絡流 24 題」太空飛行計劃 最大權閉合圖
#6001. 「網絡流 24 題」太空飛行計劃
內存限制:256 MiB時間限制:1000 ms標準輸入輸出 題目類型:傳統評測方式:Special Judge 上傳者: 匿名 提交提交記錄統計討論測試數據題目描述
W 教授正在為國家航天中心計劃一系列的太空飛行。每次太空飛行可進行一系列商業性實驗而獲取利潤。現已確定了一個可供選擇的實驗集合 E={E1,E2,?,Em} E = \{ E_1, E_2, \cdots, E_m \}E={E?1??,E?2??,?,E?m??},和進行這些實驗需要使用的全部儀器的集合 I={I1,I2,?,In} I = \{ I_1, I_2, \cdots, I_n \}I={I?1??,I?2??,?,I?n??}。實驗 Ej E_jE?j?? 需要用到的儀器是 I II 的子集 Rj⊆I R_j \subseteq IR?j??⊆I。
配置儀器 Ik I_kI?k?? 的費用為 ck c_kc?k?? 美元。實驗 Ej E_jE?j?? 的贊助商已同意為該實驗結果支付 pj p_jp?j?? 美元。W 教授的任務是找出一個有效算法,確定在一次太空飛行中要進行哪些實驗並因此而配置哪些儀器才能使太空飛行的凈收益最大。這裏凈收益是指進行實驗所獲得的全部收入與配置儀器的全部費用的差額。
對於給定的實驗和儀器配置情況,編程找出凈收益最大的試驗計劃。
輸入格式
第 1 11 行有 2 22 個正整數 m mm 和 n nn。m mm 是實驗數,n nn 是儀器數。接下來的 m mm 行,每行是一個實驗的有關數據。第一個數贊助商同意支付該實驗的費用;接著是該實驗需要用到的若幹儀器的編號。最後一行的 n nn 個數是配置每個儀器的費用。
輸出格式
第 1 11 行是實驗編號,第 2 22 行是儀器編號,最後一行是凈收益。
樣例
樣例輸入
2 3
10 1 2
25 2 3
5 6 7
樣例輸出
1 2
1 2 3
17
數據範圍與提示
1≤n,m≤50
題目鏈接:https://loj.ac/problem/6001
題意:有m個太空飛行計劃,執行一個計劃可以獲得xi的贊助,但是每個飛行計劃需要一些儀器。每個儀器配置需要花費xi。求執行一些天空飛行計劃使得收益最大。
思路:最大權閉合圖。對於一個割割成2個頂點集合,讓其中一個的執行飛行計劃,另一個不執行。這裏不妨假設包含s的集合執行飛行計劃,包含t的集合不執行。由於是最小割,就需要搞清楚最小化的是什麽。看起來損失是一個比較合理的解釋。先不考慮儀器和計劃之間的關系,而是先考慮怎麽歸到最小割這個問題上面。每一個儀器和計劃對應一個頂點。如果儀器和s相連,也就是配置了儀器,產生了損失,因此和s之間連一條容量為配置花費為容量的邊。由於如果和t相連,也就是不配置儀器,就沒有收益也沒有損失。如果計劃和s相連,也就是執行了計劃,就可以看成是產生了負的損失,因為最大流最小割定理用最大流求解的話,是不能有容量為負的邊。因此,我們認為計劃不執行則損失了收益。接下來明天發。。。
代碼:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<queue> #include<stack> #include<vector> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=2e2+100,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7; const ll INF=1e18+7; priority_queue<P,vector<P>,greater<P> >q; struct edge { int from,to; int cap; int rev; }; int n; vector<edge>G[maxn]; int level[maxn]; int iter[maxn]; void addedge(int u,int v,int c) { edge e; e.from=u,e.to=v,e.cap=c,e.rev=G[v].size(); G[u].push_back(e); e.from=v,e.to=u,e.cap=0,e.rev=G[u].size()-1; G[v].push_back(e); } int bfs(int s,int t) { memset(level,-1,sizeof(level)); queue<int>q; level[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<G[u].size(); i++) { edge e=G[u][i]; if(e.cap>0&&level[e.to]<0) { level[e.to]=level[u]+1; q.push(e.to); } } } return level[t]!=-1?1:0; } int dfs(int u,int t,int f) { if(u==t||f==0) return f; int flow=0; for(int &i=iter[u]; i<G[u].size(); i++) { edge e=G[u][i]; if(e.cap>0&&level[u]+1==level[e.to]) { int d=dfs(e.to,t,min(f,e.cap)); if(d>0) { G[u][i].cap-=d; G[e.to][e.rev].cap+=d; flow+=d; f-=d; if(f==0) break; } } } return flow; } int max_flow(int s,int t) { int flow=0; while(bfs(s,t)) { memset(iter,0,sizeof(iter)); flow+=dfs(s,t,inf); } return flow; } int a[maxn],b[maxn]; int main() { int m,n; scanf("%d%d",&m,&n); int s=0,t=n+m+1; int ans=0; for(int i=1; i<=m; i++) { scanf("%d",&a[i]); ans+=a[i]; addedge(s,i,a[i]); int num=0; char ch; do { ch=getchar(); if(ch==‘ ‘||ch==‘\n‘) { if(num) addedge(i,num+m,inf); num=0; } else num=num*10+(ch-‘0‘); } while(ch!=‘\n‘); } for(int i=1; i<=n; i++) { scanf("%d",&b[i]); addedge(i+m,t,b[i]); } ans-=max_flow(s,t); for(int i=1;i<=m;i++) if(level[i]!=-1) printf("%d ",i); cout<<endl; for(int i=1;i<=n;i++) if(level[i+m]!=-1) printf("%d ",i); cout<<endl; printf("%d\n",ans); }最大權閉合圖
LibreOJ #6001. 「網絡流 24 題」太空飛行計劃 最大權閉合圖