P4016 负载平衡问题(最小费用最大流)
P4016 負載平衡問題
題目描述
GG 公司有 nn 個沿鐵路運輸線環形排列的倉庫,每個倉庫存儲的貨物數量不等。如何用最少搬運量可以使 nn 個倉庫的庫存數量相同。搬運貨物時,只能在相鄰的倉庫之間搬運。
輸入格式
文件的第 11 行中有 11 個正整數 nn,表示有 nn 個倉庫。
第 22 行中有 nn 個正整數,表示 nn 個倉庫的庫存量。
輸出格式
輸出最少搬運量。
輸入輸出樣例
輸入 #1復制
5 17 9 14 16 4輸出 #1復制
11說明/提示
\(1<=n<=100\)
思路:
「問題分析」
轉化為供求平衡問題,用最小費用最大流解決。
「建模方法」
首先求出所有倉庫存貨量平均值,設第i個倉庫的盈余量為A[i],A[i] = 第i個倉庫原有存貨量 – 平均存貨量。建立二分圖,把每個倉庫抽象為兩個節點Xi和Yi。增設附加源S匯T。
1、如果A[i]>0,從S向Xi連一條容量為A[i],費用為0的有向邊。
2、如果A[i]<0,從Yi向T連一條容量為-A[i],費用為0的有向邊。
3、每個Xi向兩個相鄰頂點j,從Xi到Xj連接一條容量為無窮大,費用為1的有向邊,從Xi到Yj連接一條容量為無窮大,費用為1的有向邊。
求最小費用最大流,最小費用流值就是最少搬運量。
「建模分析」
計算出每個倉庫的盈余后,可以把問題轉化為供求問題。建立供求網絡,把二分圖X集合中所有節點看做供應節點,Y集合所有節點看做需求節點,在能一次搬運滿足供需的Xi和Yj之間連接一條費用為1的有向邊,表示搬運一個單位貨物費用為1。另外還要在Xi與相鄰的Xj之間連接邊,表示貨物可以暫時搬運過去,不立即滿足需求,費用也為1。最大流滿足了所有的盈余和虧損供求平衡,最小費用就是最少搬運量。
以上拷貝自:http://hzwer.com/1955.html 這位聚聚博主。
我的代碼:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <vector> #include <iomanip> #define ALL(x) (x).begin(), (x).end() #define sz(a) int(a.size()) #define rep(i,x,n) for(int i=x;i<n;i++) #define repd(i,x,n) for(int i=x;i<=n;i++) #define pii pair<int,int> #define pll pair<long long ,long long> #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define MS0(X) memset((X), 0, sizeof((X))) #define MSC0(X) memset((X), '\0', sizeof((X))) #define pb push_back #define mp make_pair #define fi first #define se second #define eps 1e-6 #define gg(x) getInt(&x) #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl #define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c)) #define du2(a,b) scanf("%d %d",&(a),&(b)) #define du1(a) scanf("%d",&(a)); using namespace std; typedef long long ll; ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} ll lcm(ll a, ll b) {return a / gcd(a, b) * b;} ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;} void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}} void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}inline void getInt(int* p); const int maxn = 1000010; // const int inf = 0x3f3f3f3f; /*** TEMPLATE CODE * * STARTS HERE ***/ const int MAX_N = 1000; const int MAX_M = 10000; const int inf = 0x3f3f3f3f; // int s, t; struct edge {int v, c, w, next; // v 表示邊的另一個頂點,c 表示當前剩余容量,w 表示單位流量費用 } e[MAX_M]; int p[MAX_N], s, t, eid; // s 表示源點,t 表示匯點,需要在進行 costflow 之前設置完畢 void init() {memset(p, -1, sizeof(p));eid = 0; } void insert(int u, int v, int c, int w) {e[eid].v = v;e[eid].c = c;e[eid].w = w;e[eid].next = p[u];p[u] = eid++; } void addedge(int u, int v, int c, int w) {insert(u, v, c, w);insert(v, u, 0, -w); } bool inq[MAX_N]; int d[MAX_N]; // 如果到頂點 i 的距離是 0x3f3f3f3f,則說明不存在源點到 i 的最短路 int pre[MAX_N]; // 最短路中連向當前頂點的邊的編號 bool spfa() { // 以源點 s 為起點計算單源最短路,如果不存在從 s 到 t 的路徑則返回 false,否則返回 truememset(inq, 0, sizeof(inq));memset(d, 0x3f, sizeof(d));memset(pre, -1, sizeof(pre));d[s] = 0;inq[s] = true;queue<int> q;q.push(s);while (!q.empty()) {int u = q.front();q.pop();inq[u] = false;for (int i = p[u]; i != -1; i = e[i].next) {if (e[i].c) { //注意這個條件!!!spfa這里是以w求最短路的,但仍然不能忽略容量c的考慮!int v = e[i].v;if (d[u] + e[i].w < d[v]) {d[v] = d[u] + e[i].w;pre[v] = i;if (!inq[v]) {q.push(v);inq[v] = true;}}}}}return pre[t] != -1; }int min_cost_flow() { // 計算最小費用最大流int ret = 0; // 累加和while (spfa()) {int flow = inf;for (int i = t; i != s; i = e[pre[i] ^ 1].v) {flow = min(e[pre[i]].c, flow); // 計算當前增廣路上的最小流量}for (int i = t; i != s; i = e[pre[i] ^ 1].v) {e[pre[i]].c -= flow; //容量是一定要跟著變化的,畢竟要繼續循環使用spfa來更新下一條“能走的(看容量)”最短路。e[pre[i] ^ 1].c += flow;ret += e[pre[i]].w * flow;}}return ret; }int n; int a[MAX_N]; int main() {//freopen("D:\\code\\text\\input.txt","r",stdin);//freopen("D:\\code\\text\\output.txt","w",stdout);init();du1(n);int sum = 0;repd(i, 1, n){du1(a[i]);sum += a[i];}sum /= n;s = 0;t = n * 2 + 1;repd(i, 1, n){a[i] -= sum;if (a[i] > 0){addedge(s, i, a[i], 0);} else{addedge(i + n, t, -a[i], 0);}if (i - 1 > 0){addedge(i, i + n - 1, inf, 1);addedge(i, i - 1, inf, 1);}if (i + 1 < n + 1){addedge(i, i + n + 1, inf, 1);addedge(i, i + 1, inf, 1);}}addedge(1, n, inf, 1);addedge(1, n * 2, inf, 1);addedge(n, 1, inf, 1);addedge(n, 1 + n, inf, 1);printf("%d\n", min_cost_flow() );return 0; }inline void getInt(int* p) {char ch;do {ch = getchar();} while (ch == ' ' || ch == '\n');if (ch == '-') {*p = -(getchar() - '0');while ((ch = getchar()) >= '0' && ch <= '9') {*p = *p * 10 - ch + '0';}}else {*p = ch - '0';while ((ch = getchar()) >= '0' && ch <= '9') {*p = *p * 10 + ch - '0';}} }轉載于:https://www.cnblogs.com/qieqiemin/p/11629190.html
總結
以上是生活随笔為你收集整理的P4016 负载平衡问题(最小费用最大流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#中的常用ToString()方法总结
- 下一篇: 洛谷 P2765 魔术球问题 (dini