题目大意:
米尔克在一个养猪场工作,养猪场共有M间有锁的猪舍(1 ≤ M ≤ 1,000),但是米尔克没有猪舍的锁,所都在顾客手上,某一天共有N位顾客来养猪场买猪(1 ≤ N ≤ 100,以上所有编号(包括猪舍)都是从1开始编号的),顾客一个接一个的来买猪(是有先后顺序的,不是两位顾客同时买的那种),每位顾客手上都有若干把钥匙,只能买这些钥匙能打开的猪舍中买猪,顾客会先将能打开的猪舍都打开,然后买一定数量的猪(数量确定),买完后这些打开门的猪舍中的猪可以由米尔克自由调配(每个猪舍中的猪的数量无上限,可以为0头最都能有1,000头),交易结束后这些猪舍关闭锁上,再迎接下一位顾客。
现只有一个测例,会给出猪舍数、顾客数、每个猪舍中猪的初始数量、每位顾客(按顺序)的钥匙数以及没把钥匙对应的猪舍的编号和购猪数量,现求当天米尔克能卖出的最大猪的数量。
注释代码:
/*
* Problem ID : POJ 1459 Power Network
* Author : Lirx.t.Una
* Language : C++
* Run Time : 0 ms
* Run Memory : 232 KB
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
//maximum number of customers
//顾客的最大数量
//最多为100人,下标0为源点,100 + 1为汇点
//下标从0开始共有102个点
#define MAXCUSTN 102
//maximum number of pig-houses
//猪圈的最大数量
//下标从1到1,000
#define MAXPIGHN 1001
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
using namespace std;
//flw[u][v]表示顾客u传到顾客v的猪的最大数量
//为一残余网络
int flw[MAXCUSTN][MAXCUSTN];
//level
//表示各点的DINIC层级,源点为0,多一条边层级加1
//从源点开始计算
char lev[MAXCUSTN];
int pgh[MAXPIGHN];//pig-house,pgh[i]表示i号猪圈中的猪的初始数量
//previous customer of ith pig-house
//pre[i]表示访问i号猪圈的上一个顾客的编号
char pre[MAXPIGHN];
bool
bfs( int src, int des ) {//对当前残余网络构造层级
//同时也铺设了可能多条增广路径
queue<int> que;
int u, v;
memset(lev, -1, ( des + 1 ) * sizeof(char));
lev[src] = 0;//源点层级为0,无点层级为-1
que.push(src);
while ( !que.empty() ) {
u = que.front();
que.pop();
for ( v = src; v <= des; v++ )//即使汇点也被构造到了也需继续构造
//因为在某一轮残余网络中汇点可能只是某一个中间结点
//层级不一定是最大的!!!
if ( -1 == lev[v] && flw[u][v] ) {
lev[v] = lev[u] + 1;//下一层比上一层大一
que.push(v);
}
}
if ( -1 == lev[des] )//汇点不在网络中,说明不存在增广路径
return false;
return true;
}
int
dinic( int u, int des, int lftf ) {//DINIC算法求最大流(递归)
//表示现在以u为起点的残余网络中剩余可支配的流量为lftf
//即left flow
//即从u到des的一条增广路径的可用流量只有lftf这么多了
if ( u == des )//若已到达汇点,则剩余的流量就是最大流量
return lftf;
//final maximum flow,最终求取的以u为源点以des为汇点的网络的最大流
int fimaxflw;
//next maximum flow
//最终求取的以u的下一层结点为源点以des为汇点的网络的最大流
int nxmaxflw;
int v;//u的下一层结点
for ( fimaxflw = 0, v = 0; v <= des; v++ )
if ( lev[v] == lev[u] + 1 && flw[u][v] ) {//表示u->v为一条可行流
//递归求取某一分支子网络的最大流
nxmaxflw = dinic( v, des, MIN( lftf, flw[u][v] ) );
flw[u][v] -= nxmaxflw;//前向弧减去
flw[v][u] += nxmaxflw;//后向弧加上,构造下一轮残余网络
//剩余流量需要减去次分支网络的最大流作为其它分支网络的剩余流量
lftf -= nxmaxflw;
fimaxflw += nxmaxflw;//最终流量累加
}
return fimaxflw;
}
int
main() {
int m, n;//猪圈数和顾客数
int tot;//total number of pigs,猪的总数,作为猪的数量的infinity
int i;//计数变量
int cus;//customer,当前顾客的编号
int nk;//number of keys,当前顾客所拥有的猪圈钥匙数量
int pth;//ith pig-house,猪圈的编号
int src, des;//源点和汇点
int ans;
scanf("%d%d", &m, &n);
src = 0;
des = n + 1;
for ( tot = 0, pth = 1; pth <= m; pth++ ) {
scanf("%d", pgh + pth);
tot += pgh[pth];
}
//初始残余网络,定义各条弧的流量上限
for ( cus = 1; cus <= n; cus++ ) {
scanf("%d", &nk);
while ( nk-- ) {
scanf("%d", &pth);
//若该猪圈没被顾客访问过
//则只能从源点流向该顾客
if ( !pre[pth] )
flw[src][cus] += pgh[pth];
else//否则从上一访问过该猪圈的顾客那里流向当前顾客
//因为访问一次后开门的猪圈中的猪可以随意重新分布
//因此某一猪圈中猪的数量没有上限
flw[ pre[pth] ][cus] = tot;
pre[pth] = cus;//更新之前访问该猪圈的顾客的编号
}
scanf("%d", &flw[cus][des]);//顾客购买总数流向汇点
}
ans = 0;
//没跟新一次残余网络就重新计算一下层级
while ( bfs( src, des ) )//计算层级
ans += dinic( src, des, tot );//更新网络并返回可行最大流
printf("%d\n", ans);
return 0;
}
无注释代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define MAXCUSTN 102
#define MAXPIGHN 1001
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
using namespace std;
int flw[MAXCUSTN][MAXCUSTN];
char lev[MAXCUSTN];
int pgh[MAXPIGHN];
char pre[MAXPIGHN];
bool
bfs( int src, int des ) {
queue<int> que;
int u, v;
memset(lev, -1, ( des + 1 ) * sizeof(char));
lev[src] = 0;
que.push(src);
while ( !que.empty() ) {
u = que.front();
que.pop();
for ( v = src; v <= des; v++ )
if ( -1 == lev[v] && flw[u][v] ) {
lev[v] = lev[u] + 1;
que.push(v);
}
}
if ( -1 == lev[des] )
return false;
return true;
}
int
dinic( int u, int des, int lftf ) {
if ( u == des )
return lftf;
int fimaxflw;
int nxmaxflw;
int v;
for ( fimaxflw = 0, v = 0; v <= des; v++ )
if ( lev[v] == lev[u] + 1 && flw[u][v] ) {
nxmaxflw = dinic( v, des, MIN( lftf, flw[u][v] ) );
flw[u][v] -= nxmaxflw;
flw[v][u] += nxmaxflw;
lftf -= nxmaxflw;
fimaxflw += nxmaxflw;
}
return fimaxflw;
}
int
main() {
int m, n;
int tot;
int i;
int cus;
int nk;
int pth;
int src, des;
int ans;
scanf("%d%d", &m, &n);
src = 0;
des = n + 1;
for ( tot = 0, pth = 1; pth <= m; pth++ ) {
scanf("%d", pgh + pth);
tot += pgh[pth];
}
for ( cus = 1; cus <= n; cus++ ) {
scanf("%d", &nk);
while ( nk-- ) {
scanf("%d", &pth);
if ( !pre[pth] )
flw[src][cus] += pgh[pth];
else
flw[ pre[pth] ][cus] = tot;
pre[pth] = cus;
}
scanf("%d", &flw[cus][des]);
}
ans = 0;
while ( bfs( src, des ) )
ans += dinic( src, des, tot );
printf("%d\n", ans);
return 0;
}
单词解释:
Mirko:人名,米尔克
locked:adj, 上锁的
unlocked:adj, 无锁的,打开着的
unlock:vt, 开锁
precisely:adv, 精确地,恰好
redistribute:vt, 重新分配,再分区
remaining:adj, 剩下的unlimited:adj, 无限制的
number:vt, 编号