点我查看原题链接
这道题的模型我们首先来简化一下:顾客来养猪场打开他有钥匙的猪圈,之后买走一些猪,这之后老板可以重新调整已经打开的猪圈的内猪的数量。然后下一个顾客来……
假设就这个流程建图,我们可以这样建:
1.首先对于每一轮顾客,每个猪圈就对应一个点,首先源点和第一轮的所有猪圈连接,容量是初始数量。
2.之后每一轮的猪圈i都向下一轮的猪圈i连一条边,容量INF,这一轮被打开的猪圈,向其他被打开的猪圈在下一轮的节点连一条容量无线的边,表示这些猪圈里的猪可以流通。
3.每个顾客在他对应的轮里,连接他能打开的所有猪圈,容量INF,每个顾客再向汇点连一条边,容量是他的购买上限。
这样求下来的最大流就是答案。但是这样建图明显边太多了,以下是一个简化网络流模型的通则:
1.如果有若干个节点的来源都相同,那么他们可以变成一个节点
2.如果有若干个节点的去向都相同,他们也可以变成一个
3.如果有两个节点u,v,他们之间存在边,边的方向是u到v,而且边的容量是INF同时u是v的唯一来源,那么这两个点可以当作一个
有了这三个法则,我们现在谈简化后的模型。
每个顾客用一个点表示,先从源点连一条边,这条边的容量是所有这个顾客能打开的猪圈中猪的综合
如果一个猪圈被打开了n次,对于所有的顾客都向他的下一个顾客连一条边。
每个顾客再向汇点连一条边,容量是购买上限。
这样就减少了大部分的节点,笔者建议画一下这个简化过程,很有助于理解:
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn = 1001;
const int INF = 0x7f7f7f7f;
int n,m;
struct edge{int to,cap,rev;};
vector<edge>G[maxn];
bool used[maxn];
int enterloop[maxn][maxn];
bool getthrough[maxn][maxn];
int pigwant[maxn];
int sink,sunk;
int pig[1001];
int first[1010];
void add_edge(int from,int to,int cap)
{
G[from].push_back((edge){to,cap,G[to].size()});
G[to].push_back((edge){from,0,G[from].size()-1});
}
int dfs(int v,int t, int f)
{
if(v==t) return f;
used[v]=true;
for(int i=0;i<G[v].size();i++)
{
edge& e=G[v][i];
if(!used[e.to]&&e.cap>0)
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int flow=0;
while(true)
{
memset(used,0,sizeof(used));
int f=dfs(s,t,INF);
if(f==0) return flow;
flow+=f;
}
}
void make_map()
{
int start=0;
int end=m+1;
for(int i=1;i<=n;i++)
{
add_edge(start,first[i],pig[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<enterloop[i][0];j++)
{
if(!getthrough[enterloop[i][j]][enterloop[i][j+1]])
{
add_edge(enterloop[i][j],enterloop[i][j+1],INF);
getthrough[enterloop[i][j]][enterloop[i][j+1]]=true;
}
}
}
for(int i=1;i<=m;i++)
{
add_edge(i,end,pigwant[i]);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
while(cin>>n>>m)
{
sink = 0;
sunk = m+1;
memset(first,0,sizeof(first));
for(int i=1;i<=n;i++)
{
cin>>pig[i];
//sink+=pig[i];
}
int a,b,k;
for(int i = 1;i<=m;i++)
{
cin>>a;
while(a--)
{
cin>>k;
enterloop[k][++enterloop[k][0]]=i;
if(first[k]==0)
first[k]=i;
}
cin>>pigwant[i];
//add_edge(i,sunk,b);
}
make_map();
cout<<max_flow(sink,sunk)<<endl;
}
return 0;
}