题目大意:是有M个猪圈,N个顾客,顾客要买猪,神奇的是顾客有一些猪圈的钥匙而主人MIRKO却没有钥匙,多么神奇?顾客可以在打开的猪圈购买任意数量的猪,只要猪圈里有足够数量的猪。而且当顾客打开猪圈后mirko就可以在打开的猪圈之间任意调整猪的数量,(顾客走了之后猪圈要关闭)。问mirko怎样做能使顾客买到最多的猪
思路如下:(也是查的,具体原理和原因明天更新)
1.取超级源点和超级汇点;
2.当猪圈被第一次打开时,在源点与当前顾客之间连接一条边,容量为该猪圈的猪的头数;
3.当某个猪圈 不是被第一次打开时,在上一个打开该猪圈的顾客与当前打开该猪圈的顾客之间连接一条边,容量为无穷大;
4.在每个顾客与汇点之间连接一条边,容量为该顾客要买猪的头数。
构图完成后套用Edmonds-Karp算法的模版即可,当然用Dinic算法也可以解出。
【更新】/**************************************
想一下这题,其实猪圈应该是源点,顾客应该是汇点顾客,买猪就相当于从源点往汇点运东西,是吧?但是有点不对,那个边的容量是什么呢?应该是猪圈的猪的数量吗?嗯,想一下好像是的,但是还有一个数据——顾客期望购买的猪的数量这个数据放到哪里呢?这样一想我们建图肯定是有问题的,是吧?而且这样一做之后我们的图中就有很多源点和汇点了,这个问题当然很好解决,加一个超级源点,和一个超级汇点这样问题貌似解决了,我们可以把顾客的期望购买猪的数量和超级汇点建立一条边,容量为顾客期望购买的猪的数量,源点和猪圈之间也连一条边,边权为猪圈猪的数量,这样做对吗?好像是对的但是还有一个问题没有解决,顾客有多个猪圈的钥匙,买完猪后mirko可以调整猪圈猪的数量,这个特性没能在图中显示出来。其实这个特性也好解决当一些猪圈被同一个人打开后这些猪圈之间的猪就可以*流通了,是吧?也就相当于可以任意调整猪圈内猪的数量了,于是我们可以在对于2个连续的顾客打开的猪圈之间有公共的猪圈的顾客之间连一条边,当然是要从先买猪的顾客连向后买猪的顾客,这样猪就能流通了是吧?,因为顾客打开的猪圈的猪是可以流向顾客的,然后顾客又流向另一个顾客,不就相当于先打开的猪圈的猪可以往后来的顾客打开的猪圈的猪进行流通了吗?(当然条件是他们打开过同一个猪圈)这样图就建好了。终于建完了图。看看我用第一组样例画的图吧(很挫啊)。
看看这个图总觉得有些东西很多余,看看源点到猪圈的边和猪圈到顾客的边是不是有点重复的感觉?其实这里有些边是可以合并的,看一下从猪圈2分出的2条边一个是到顾客1,一个是到顾客3的边但是顾客1到3又有一条边权为INF(无穷大的边)很明显2->3的边可以由2->1->3这2两条边替代,所以
2->3这条边其实是多余的,那么我们可以把这条边去掉,同理还有别的一些边也是可以去掉的,去掉后的图如下
很神奇吧?再仔细看一下图,是不是发现,猪圈是多余的了呢?我们可以吧猪圈1 和 2 合并因为它们都是指向顾客1的然后合并2条边,同理猪圈3也可以省略。省略后的图如下
现在再看一下我百度的建图思路:
.取超级源点和超级汇点;
2.当猪圈被第一次打开时,在源点与当前顾客之间连接一条边,容量为该猪圈的猪的头数;
3.当某个猪圈 不是被第一次打开时,在上一个打开该猪圈的顾客与当前打开该猪圈的顾客之间连接一条边,容量为无穷大;
4.在每个顾客与汇点之间连接一条边,容量为该顾客要买猪的头数。
哈哈,是不是一样一样的了,现在理解为什么要这样建图了吧?
******************************************/
我的代码如下,有详细的注释
/*********
PRO: POJ 1149
TIT: Pigs
DAT: 2013-08-13-08.09
AUT: UKean
EMA: huyocan@163.com
*********/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 1e9
using namespace std;
queue<int> que;//广搜需要使用的队列
int M,N;//M是边数,N是点数
int s,t;//源点和汇点
int flow[115][115];//流量
int p[105];//广搜记录路径的父节点数组
int a[105];//路径上的最小残量
int cap[115][115];//容量网络
int zhujuan[1007];//猪圈记录猪圈里猪的个数
int tag[1007];//标记这个猪圈有没有打开,打开了上次是被谁打开的 int read()//读入建图部分
{
int m,n,ca;
if(!(cin>>m>>n)) return 0;//读入结束
M=n+1;//节点的个数
s=0,t=M;//超级源点,超级汇点
memset(cap,0,sizeof(cap));//初始化容量网络
for(int i=1;i<=m;i++)//m个猪圈
cin>>zhujuan[i];//猪圈的容量
memset(tag,0,sizeof(tag));//初始化标记
for(int i=1;i<=n;i++)
{
int num,temp;
cin>>num;//读入第i个的钥匙数量
for(int j=0;j<num;j++)
{
cin>>temp;//读入猪圈的钥匙
if(!tag[temp])//如果这个猪圈是第一次打开
{
tag[temp]=i;//记录猪圈最后一个打开的人是谁
cap[s][i]+=zhujuan[temp];//让源点和顾客连一条边,注意这个要合并边权
}
else
{
cap[tag[temp]][i]=INF;//让当前的人和最后打开temp猪圈的人连一条边
tag[temp]=i;//记录猪圈最后一个打开的人是谁
}
}
cin>>ca;//顾客期望购买的猪的数
cap[i][t]+=ca;//让顾客和汇点建立一条边
}
return 1;
} int deal()//增广路算法就不具体解释了,详细的解释可以看我关于网络流的第一篇博客
// http://blog.csdn.net/hikean/article/details/9918093
{
memset(flow,0,sizeof(flow));
int ans=0;
while(1)
{
memset(a,0,sizeof(a));
a[s]=INF;
que.push(s);
while(!que.empty())
{
int u=que.front();que.pop();
for(int v=0;v<=M;v++)
if(!a[v]&&cap[u][v]-flow[u][v]>0)
{
p[v]=u;
que.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=p[u])
{
flow[p[u]][u]+=a[t];
flow[u][p[u]]-=a[t];
}
ans+=a[t];
}
cout<<ans<<endl;
return ans;
}
int main()
{
while(read())
deal();
return 0;
}