Description
Input
Output
Sample Input
3 3 3 1 10 2 1 2 2 2 1 3 3 1 2 6
Sample Output
7
题意:题目大意:有N个顾客,有M个猪圈,每个猪圈有一定的猪,在开始的时候猪圈都是关闭的,顾客来买书,顾客打开某个猪圈,可以在其中挑选一定的猪的数量,在这个顾客走后,可以在打开的猪圈中将某个猪圈的一些猪牵到另外一个打开的猪圈,然后所有的猪圈会关闭,这样下一个顾客来了继续上面的工作。
图论理解:刚开始确实不知道怎么建图。网络流也就那几个算法而已,曾有博客中的博文中说过:网络流算法是挺简单的,但是在比赛中为何还要考网络流呢?原因是建图!图论,考的大多都不是算法,因为算法,只要每个去比赛的人,都会这些算法,但是不一定每个都会建图,而建图就正好是图论需要把把实际或者抽象的东西转化为图型来解决的过程,所以建图就是图论中的一个难点。在ACM上的OJ中,难点也是这些建图,这题考的就是建图。在二分图还有最短路中我都遇到过拆点来建图,当然还有很多建图方法。而我们训练的目的如果只是学习算法,那么你可以在一个月内理解算法并解决一些简单的题目,但是一遇到建图的就得看别人的博客学习了,所以如果把建图搞定了,那么图论也就基本过关斩将了!!!
解题思路:这题给出每个猪圈的最初猪的数目,然后给出一个接一个顾客的钥匙和想买的数目,那么可以把最初猪的数目放在源点那里,然后把顾客想买的数目放在汇点那里。但是只有源点和汇点,那蹭中间的点如何来呢?因为解决中间那么点及边,这个图也就完成了。
比如:猪圈1中有8头猪,猪圈2中有7头猪,如果第一个顾客先买猪圈1中3头猪,如果第二个顾客也想买猪圈1中10头猪,那么第一个顾客可以满足自己买的要求的,可以买到3头猪,现在猪圈1中还有5头猪,而第二个顾客如果要买10头猪从猪圈1中的话,也是可以满足要求的,就是把猪圈2中7头猪放到猪圈1中,猪圈1中就有10头猪了,那么就可以满足第二个顾客的需要了。我们现在可以口算出来,但是如何建图呢,我们知道,第二个顾客,因为不知道第一个猪圈还剩多少,所以可以把第二个顾客与猪圈1连边,权值为未知。但是未知在图中是无法用网络流算法求出的,因为用EK算法和Dinic算法都需要判断边权值是否大于0,如果大于0就满足。所以这个第二个顾客与猪圈1连的边的未知量可以设置为INF(无限大),然后在网络流中进行增广路进行残量网络调整的时候就可以实现算法满足算法了。
具体建图如下:
1.每个猪圈的第一个顾客与源点连边,权值为猪圈的初始值。
2.如果不是第一个顾客,则这个猪圈的前面买的那个顾客与当前顾客连边,权值为INF。
3.当前顾客与汇点连边,权值为想买的值。
至此,网络流建图也就完成了,就可以用网络流算法直接求解了!
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <list> #include <queue> #include <string> #include <cstring> #include <map> #include <set> #define PI acos(-1.0) #define mem(a,b) memset(a,b,sizeof(a)) #define sca(a) scanf("%d",&a) #define pri(a) printf("%d\n",a) #define MM 10002 #define MN 1005 #define INF 168430090 using namespace std; typedef long long ll; int n,m,ans,d[MN],p[MN],pre[MN],Map[MN][MN]; int bfs() { mem(d,-1); queue<int>q; q.push(0); d[0]=0; while(!q.empty()) //找增广路 { int u=q.front(); q.pop(); for(int v=0; v<=n+1; v++) { if(d[v]==-1&&Map[u][v]) { d[v]=d[u]+1; //分层,越往后,层数越大 q.push(v); } } } return d[n+1]!=-1; } int dfs(int u,int Min) { int sum; if(u==n+1) return Min; for(int v=0; v<=n+1; v++) if(Map[u][v]&&d[v]==d[u]+1&&(sum=dfs(v,min(Min,Map[u][v])))) { Map[u][v]-=sum; Map[v][u]+=sum; return sum; } return 0; } void Dinic() { int tmp; while(bfs()) { while(tmp=dfs(0,INF)) ans+=tmp; } pri(ans); } int main() { int i,u,v,w,t,abc; scanf("%d%d",&m,&n); for(i=1;i<=m;i++) sca(p[i]); //记录每个猪圈的数目 for(i=1;i<=n;i++) { sca(t); while(t--) { sca(abc); //按上面思想开始建图 if(!pre[abc]) Map[0][i]+=p[abc]; //如果当前猪圈是第一个顾客,加入源点 else Map[pre[abc]][i]=INF; //如果当前猪圈是第二个顾客 pre[abc]=i; //记录下些猪圈的当前顾客,以便下一个使用 } sca(abc); Map[i][n+1]+=abc; //想买的猪加入汇点 } Dinic(); return 0; }