首页 > 代码库 > POJ--1149--PIGS【网络最大流】
POJ--1149--PIGS【网络最大流】
链接:http://poj.org/problem?id=1149
题意:迈克有一个养猪场,有m个猪圈,每个猪圈都上了锁,但是迈克没有钥匙他不能打开猪圈,要买猪的顾客一个接一个来养猪场,每个人有一些猪圈的钥匙,他们要买一定数目的猪,如果顾客要来买猪,他们会提前告诉迈克:他们所拥有的钥匙数量及对应哪些猪圈、要购买的数量,这样迈克就能安排销售计划使卖出的猪最多。
当每个顾客来的时候,将那些他拥有钥匙的猪圈门全部打开,迈克从猪圈中卖出一些猪给他们,如果迈克愿意,他可以重新分配这些被打开的猪圈中的猪,当顾客离开时猪圈再次被锁上。猪圈可容纳的猪无上限。求迈克这一天最多能卖多少猪。
昨天下午开始看网络流,看了很久,感觉理解了增广路的方法和预流推进的方法,不过一下接触的概念太多,有点迷糊。。。今天终于能做题了,看到这道题,毫无想法。。。终于知道了为什么大家都说网络流建图难,剩下的套模板就行了。我现在连模板都不会敲,更别说建图了。参考图论书上的解法和代码,一点一点理解了这道题,但是思路是书上的,代码是照着书上敲的。。
分析:
本题的关键在于如何构造一个容量网络。在本题中,容量网络的构造方法如下:
1) 将顾客看作除源点和汇点以外的结点,另设2个结点:源点和汇点;
2) 源点和每个猪圈的第一个顾客连边,边的权是开始时猪圈中猪的数目;
3) 若源点和某个结点之间有重边,则将权合并(因此源点流出的流量就是所有的猪圈能提供的猪的数目);
4) 顾客j紧跟在顾客i之后打开某个猪圈,则边<i, j>的权是+∞;这是因为,如果顾客j紧跟在顾客i 之后打开某个猪圈,那么迈克就有可能根据顾客j 的需求将其他猪圈中的猪调整到该猪圈,这样顾客j 就能买到尽可能多的猪;
5) 每个顾客和汇点之间连边,边的权是顾客所希望购买的猪的数目(因此汇点的流入量就是每个顾客所购买的猪的数目)。
这是建图的思路,是图论书上的,对熟练掌握网络最大流的人来说,剩下的就比较简单了,不过对我来说,代码还得照着书一点点敲。
算法用的是增广路的算法,没有使用DFS,我把书中自己写的队列改成了stl的队列,剩下基本都和书里一样
作为我第一道照着书敲下来的网络流题目,我把注释都写的很详细
#include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio> #include<cctype> #include<algorithm> #include<queue> #include<map> #include<set> #include<vector> #include<stack> #include<ctime> #include<cstdlib> #include<functional> #include<cmath> using namespace std; #define PI acos(-1.0) #define MAXN 100100 #define eps 1e-7 #define INF 0x7FFFFFFF #define seed 131 #define ll long long #define ull unsigned ll #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int s,t; //s源点,t汇点 int customer[110][110]; //节点间容量 int flow[110][110]; //节点间流量 int pigs[1010]; //每个猪圈中猪的数量 int last[1010]; //每个猪圈前一个顾客的序号 int n,m; //n顾客数目,m猪圈数目 void ford(){ int prev[110]; //增广路上该顶点前一个顶点的序号,初始为-2未标号,源点标号-1 int minflow[110]; //每个顶点的可改进量α minflow[0] = INF; int p; queue<int>q; while(1){ //标号法 for(int i=0;i<110;i++) prev[i] = -2; prev[0] = -1; while(!q.empty()) q.pop(); q.push(0); while(!q.empty()&&prev[t]==-2){ int v = q.front(); q.pop(); for(int i=0;i<t+1;i++){ //如果顶点i是顶点v的邻接顶点,则考虑是否对顶点i进行标号 //customer[v][i]-flow[v][i]!=0能保证顶点i是顶点v的邻接顶点且能标号 if(prev[i]==-2&&(p=customer[v][i]-flow[v][i])){ //顶点i未标号,且Cij-Fij>0 prev[i] = v; q.push(i); minflow[i] = min(minflow[v],p); } } } if(prev[t]==-2) break; //汇点t没有标号,标号法结束 int j; for(int i=prev[t],j=t;i!=-1;j=i,i=prev[i]){ //调整增广路流量 flow[i][j] = flow[i][j] + minflow[t]; flow[j][i] = -flow[i][j]; } } p = 0; for(int i=0;i<t;i++) //统计进入汇点的流量,即为最大流的流量 p = p + flow[i][t]; printf("%d\n",p); } int main(){ int k,num; //k第k个猪圈的钥匙,num每个顾客拥有钥匙的数目 int i,j; memset(last,0,sizeof(last)); memset(customer,0,sizeof(customer)); memset(flow,0,sizeof(flow)); scanf("%d%d",&m,&n); s = 0; t = n + 1; for(i=1;i<=m;i++) scanf("%d",&pigs[i]); for(i=1;i<=n;i++){ //构造网络流 scanf("%d",&num); for(j=0;j<num;j++){ scanf("%d",&k); if(last[k]==0) //第i个顾客是第k个猪圈的第一个顾客 customer[s][i] = customer[s][i] + pigs[k]; else //顾客i紧跟在顾客last[k]后面打开第k个猪圈 customer[last[k]][i] = INF; last[k] = i; } scanf("%d",&customer[i][t]); //每个顾客到汇点的边,权值为顾客购买猪的数量 } ford(); return 0; }