題目鏈接:POJ 1149 PIGS
PIGS
Description
Mirko works on a pig farm that consists of M locked pig-houses and Mirko can't unlock any pighouse because he doesn't have the keys. Customers come to the farm one after another. Each of them has keys to some pig-houses and wants to buy a certain number of
pigs.
All data concerning customers planning to visit the farm on that particular day are available to Mirko early in the morning so that he can make a sales-plan in order to maximize the number of pigs sold. More precisely, the procedure is as following: the customer arrives, opens all pig-houses to which he has the key, Mirko sells a certain number of pigs from all the unlocked pig-houses to him, and, if Mirko wants, he can redistribute the remaining pigs across the unlocked pig-houses. An unlimited number of pigs can be placed in every pig-house. Write a program that will find the maximum number of pigs that he can sell on that day. Input
The first line of input contains two integers M and N, 1 <= M <= 1000, 1 <= N <= 100, number of pighouses and number of customers. Pig houses are numbered from 1 to M and customers are numbered from 1 to N.
The next line contains M integeres, for each pig-house initial number of pigs. The number of pigs in each pig-house is greater or equal to 0 and less or equal to 1000. The next N lines contains records about the customers in the following form ( record about the i-th customer is written in the (i+2)-th line): A K1 K2 ... KA B It means that this customer has key to the pig-houses marked with the numbers K1, K2, ..., KA (sorted nondecreasingly ) and that he wants to buy B pigs. Numbers A and B can be equal to 0. Output
The first and only line of the output should contain the number of sold pigs.
Sample Input 3 3 3 1 10 2 1 2 2 2 1 3 3 1 2 6 Sample Output 7 Source |
分析:
本題關鍵在如何構圖,構好圖就可以套模板直接求解了。其實大部分的網絡流題目只要能夠圖就基本簡單了。不過構圖是個費腦力的過程。
構圖方法如下:
(1)設一個源點s、一個匯點t,
(2)將顧客看做中間節點,
(3)s和每個豬圈的第一個顧客連邊,容量爲開始時豬圈中豬的數目。如果和某個節點有重邊,則將容量值合併(因爲源點流出的流量就是所有豬圈能提供的豬的數目),
(4)顧客j緊跟在顧客i之後打開某個豬圈,則邊<i, j>的容量爲INF,因爲如果顧客j緊跟顧客i之後打開某個豬圈,那麼邁克就有可能根據顧客j的要求將其他豬圈中的豬調整到該豬圈,這樣顧客j就能買到儘可能多的豬,
(5)每個顧客和匯點之間連邊,邊的容量是顧客所希望買的豬的數目。
完成構圖後,可以進行最大流的求解了。三種方法
(1)EK算法,最短增廣路算法(即先用BFS求增廣路,然後再增廣,但是每次都要BFS,時間複雜度不好。
實現如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define maxn 1010
#define INF 0x3f3f3f3f
int ans, s, t, n;
int a[maxn], pre[maxn];
int flow[maxn][maxn];
int cap[maxn][maxn];
void EK()
{
queue<int> q;
memset(flow, 0, sizeof(flow));
ans = 0;
while(1)
{
memset(a, 0, sizeof(a));
a[s] = INF;
q.push(s);
while(!q.empty()) //bfs找增廣路徑
{
int u = q.front();
q.pop();
for(int v = 1; v <= n; v++)
if(!a[v] && cap[u][v] > flow[u][v])
{
pre[v] = u;
q.push(v);
a[v] = min(a[u], cap[u][v]-flow[u][v]);
}
}
if(a[t] == 0) break;
for(int u = t; u != s; u = pre[u]) //改進網絡流
{
flow[pre[u]][u] += a[t];
flow[u][pre[u]] -= a[t];
}
ans += a[t];
}
}
int main()
{
//freopen("poj_1149.txt", "r", stdin);
int m, x, A, B, pig[maxn], pre[maxn];
memset(cap, 0, sizeof(cap));
scanf("%d%d", &m, &n);
memset(pre, -1, sizeof(pre));
s = 0; t = n+1;
for(int i = 1; i <= m; i++)
scanf("%d", &pig[i]);
for(int i = 1; i <= n; i++)
{
scanf("%d", &A);
for(int j = 0; j < A; j++)
{
scanf("%d", &x);
if(pre[x] == -1)
cap[s][i] += pig[x];
else
cap[pre[x]][i] = INF;
pre[x] = i;
}
scanf("%d", &cap[i][t]);
}
n += 2;
EK();
printf("%d\n", ans);
return 0;
}
(2)Dinic算法。與EK同屬SAP算法, 利用層次圖來找最短路,然後用DFS增廣。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define maxn 1010
#define INF 0x3f3f3f3f
struct Edge
{
int from, to, cap;
};
vector<Edge> EG;
vector<int> G[maxn];
int n, s, t, ans, d[maxn], cur[maxn];
void addEdge(int from, int to, int cap)
{
EG.push_back((Edge){from, to, cap});
EG.push_back((Edge){to, from, 0});
int x = EG.size();
G[from].push_back(x-2);
G[to].push_back(x-1);
}
bool bfs()
{
memset(d, -1, sizeof(d));
queue<int> q;
q.push(s);
d[s] = 0;
while(!q.empty())
{
int x = q.front();
q.pop();
for(int i = 0; i < G[x].size(); i++)
{
Edge& e = EG[G[x][i]];
if(d[e.to] == -1 && e.cap > 0)
{
d[e.to] = d[x]+1;
q.push(e.to);
}
}
}
return (d[t]!=-1);
}
int dfs(int x, int a)
{
if(x == t || a == 0) return a;
int flow = 0, f;
for(int& i = cur[x]; i < G[x].size(); i++)
{
Edge& e = EG[G[x][i]];
if(d[x]+1 == d[e.to] && (f = dfs(e.to, min(a, e.cap))) > 0)
{
e.cap -= f;
EG[G[x][i]^1].cap += f;
flow += f;
a -= f;
if(a == 0) break;
}
}
return flow;
}
void Dinic()
{
ans = 0;
while(bfs())
{
memset(cur, 0, sizeof(cur));
ans += dfs(s, INF);
}
}
int main()
{
//freopen("poj_1149.txt", "r", stdin);
int m, x, A, B, pig[maxn], pre[maxn];
scanf("%d%d", &m, &n);
memset(pre, -1, sizeof(pre));
s = 0; t = n+1;
for(int i = 1; i <= m; i++)
scanf("%d", &pig[i]);
for(int i = 1; i <= n; i++) {
scanf("%d", &A);
for(int j = 0; j < A; j++) {
scanf("%d", &x);
if(pre[x] == -1)
addEdge(s, i, pig[x]);
else
addEdge(pre[x], i, INF);
pre[x] = i;
}
scanf("%d", &B);
addEdge(i, t, B);
}
n += 2;
Dinic();
printf("%d\n", ans);
return 0;
}
(3)ISAP,改進ISAP算法,是目前最好算法。也是通過層次網絡找最短路,但是反向BFS,且BFS只需要運行一次
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define maxn 1010
#define INF 0x3f3f3f3f
struct Edge
{
int from, to, cap, flow;
};
vector<Edge> EG;
vector<int> G[maxn];
int n, s, t, ans, d[maxn], cur[maxn], p[maxn], num[maxn];
bool vis[maxn];
void addEdge(int from, int to, int cap)
{
EG.push_back((Edge){from, to, cap, 0});
EG.push_back((Edge){to, from, 0, 0});
int x = EG.size();
G[from].push_back(x-2);
G[to].push_back(x-1);
}
void bfs()
{
memset(vis, false, sizeof(vis));
queue<int> q;
vis[t] = true;
d[t] = 0;
q.push(t);
while(!q.empty()) {
int x = q.front();
q.pop();
for(int i = 0; i < G[x].size(); i++) {
Edge& e = EG[G[x][i]^1];
if(!vis[e.from] && e.cap > e.flow) {
vis[e.from] = true;
d[e.from] = d[x]+1;
q.push(e.from);
}
}
}
}
int augment()
{
int x = t, a = INF;
while(x != s) {
Edge& e = EG[p[x]];
a = min(a, e.cap-e.flow);
x = EG[p[x]].from;
}
x = t;
while(x != s) {
EG[p[x]].flow += a;
EG[p[x]^1].flow -= a;
x = EG[p[x]].from;
}
return a;
}
void ISAP()
{
ans =0;
bfs();
memset(num, 0, sizeof(num));
for(int i = 0; i < n; i++)
num[d[i]]++;
int x = s;
memset(cur, 0, sizeof(cur));
while(d[s] < n) {
if(x == t) {
ans += augment();
x = s;
}
bool flag = false;
for(int i = cur[x]; i < G[x].size(); i++) {
Edge& e = EG[G[x][i]];
if(e.cap > e.flow && d[x] == d[e.to]+1) {
flag = true;
p[e.to] = G[x][i];
cur[x] = i;
x = e.to;
break;
}
}
if(!flag) {
int m = n-1;
for(int i = 0; i < G[x].size(); i++) {
Edge& e = EG[G[x][i]];
if(e.cap > e.flow)
m = min(m, d[e.to]);
}
if(--num[d[x]] == 0) break;
num[d[x] = m+1]++;
cur[x] = 0;
if(x != s)
x = EG[p[x]].from;
}
}
}
void Clear()
{
EG.clear();
for(int i = 0; i < n; ++i)
G[i].clear();
}
int main()
{
//freopen("poj_1149.txt", "r", stdin);
int m, x, A, B, pig[maxn], pre[maxn];
scanf("%d%d", &m, &n);
memset(pre, -1, sizeof(pre));
s = 0; t = n+1;
for(int i = 1; i <= m; i++)
scanf("%d", &pig[i]);
for(int i = 1; i <= n; i++)
{
scanf("%d", &A);
for(int j = 0; j < A; j++)
{
scanf("%d", &x);
if(pre[x] == -1)
addEdge(s, i, pig[x]);
else
addEdge(pre[x], i, INF);
pre[x] = i;
}
scanf("%d", &B);
addEdge(i, t, B);
}
n += 2;
ISAP();
printf("%d\n", ans);
Clear();
return 0;
}
比較