AcWing 2237. 猪
作者:互联网
分析
本题还是蛮有意思的,我们来理顺一下思维过程。
我们总结一下,题目的操作。
每名顾客按顺序进行
- 将自己有钥匙的猪舍打开,从中挑选一些不超过自己想买的数量的猪。
- 同时可以将,打开的猪舍中的猪进行调整
这里面,我们需要一个逆向思维
我们考虑打开的猪舍中的所有猪都先被顾客取走,然后只拿走自己想要的部分,其余部分在自由的返回给打开的猪舍中
我们首先考虑的是,那我们是否可以直接,从顾客能打开的猪舍向顾客连一条边,接着再从顾客向所有能打开的猪舍连一条边,容量都是无穷。
不行,这显然是不对的,因为我们需要考虑顺序问题,每一位顾客是在上一位的基础上挑选的。是不是发现了什么, 我们发现,这是逐层递推的,那我们可以建立分层图,以顾客的顺序建立,这样就能一步步递推了。
好啦,这就是我们的最基础写法。
分层图+网络流
这是最慢的写法了,在POJ上跑了三百多毫秒
接下来,我们考虑优化。
第一种优化
我们考虑,当前顾客与之前的顾客有一个猪舍重合,则当前顾客就可以取到之前的顾客所能触及到的所有猪舍
这样,我们就将时序的概念破坏,变成了一个二分图多重匹配的问题。
我们可以用bitset,优化判断是否有猪舍重合与将两者的猪舍合并的这个过程。
具体过程可以看看大佬的博客。whsstory
第二种优化
我们可以发现,对于最基础的方法,我们建立分层图之后。
我们可以发现,若我们当前的顾客与之前的顾客有一个猪舍重合,则当前的顾客所能得到猪的途径,则会多一条可以从之前的顾客手中得到猪的路径。因为如果我们想一个顾客取猪时,是先将能得到的猪全部取到手,再将剩余部分任意放回猪舍。则可以想成,若当前顾客与之前顾客有一个猪舍重合,则理解为,可以从之前的顾客手中得到猪。
也就是说,若当前顾客与之前顾客有一个猪舍重合,则我们可以直接从之前的顾客直接向当前顾客连一条容量为无穷的边。
初始时,若一个猪舍没有给任何顾客过,则从源点连接一条容量为猪舍初始数量的边。再从每一个顾客想汇点连接一条容量为最多购买数量的边。
这个就是最优写法了。
其思路,可以说是从分层图中得到的,我们从分层图中发现可以顾客顺序间,互相之间的连接关系。
AC_code
分层图+网络流
#include<bits/stdc++.h>
using namespace std;
const int N = 100110,M = (200000 + 1000 + 100)*2 + 10,INF = 0x3f3f3f3f;
int h[N],e[M],ne[M],f[M],idx;
int cur[N],q[N],d[N];
bool st[1010];
int n,m,S,T;
void add(int a,int b,int c)
{
e[idx] = b,ne[idx] = h[a],f[idx] = c,h[a] = idx++;
e[idx] = a,ne[idx] = h[b],f[idx] = 0,h[b] = idx++;
}
int get(int i,int t)
{
return (t-1)*n + i;
}
bool bfs() // 创建分层图
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false; // 没有增广路
}
int find(int u, int limit) // 在残留网络中增广
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i; // 当前弧优化
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0,T = N - 1;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
add(S,i,x);
}
for(int i=1;i<=m;i++)
{
int a;scanf("%d",&a);
memset(st, 0, sizeof st);
while(a--)
{
int x;scanf("%d",&x);
st[x] = 1;
add(get(x,i),i+n*m,INF);
if(i!=m) add(i+n*m,get(x,i+1),INF);
}
int b;scanf("%d",&b);
add(i+n*m,T,b);
if(i!=m){
for(int j=1;j<=n;j++)
if(!st[j])
add(get(j,i),get(j,i+1),INF);
}
}
printf("%d\n",dinic());
return 0;
}
第二种优化
#include<bits/stdc++.h>
using namespace std;
const int N = 110,M = 20200,INF = 1e8;
int h[N],e[M],ne[M],w[M],idx;
int d[N],cur[N];
int g[1010],last[1010];
int m,n,S,T;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
e[idx]=a,ne[idx]=h[b],w[idx]=0,h[b]=idx++;
}
bool bfs()
{
queue<int> q;
memset(d,-1,sizeof d);
d[S] = 0,cur[S] = h[S],q.push(S);
while(q.size())
{
auto t = q.front();
q.pop();
for(int i=h[t];~i;i=ne[i])
{
int j = e[i];
if(d[j]==-1&&w[i])
{
d[j] = d[t] + 1;
cur[j] = h[j];
if(j==T) return 1;
q.push(j);
}
}
}
return 0;
}
int find(int u,int limit)
{
if(u==T) return limit;
int flow = 0;
for(int i=cur[u];~i&&flow<limit;i=ne[i])
{
cur[u] = i;
int j = e[i];
if(d[j]==d[u]+1&&w[i])
{
int t = find(j,min(w[i],limit-flow));
if(!t) d[j] = -1;
w[i] -= t,w[i^1] += t,flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0,flow;
while(bfs()) while(flow=find(S,INF)) r+=flow;
return r;
}
int main()
{
cin>>m>>n;
memset(h, -1, sizeof h);
S = 0,T = n + 1;
for(int i=1;i<=m;i++) cin>>g[i];
for(int i=1;i<=n;i++)
{
int a;cin>>a;
while(a--)
{
int id;cin>>id;
if(!last[id]) add(S,i,g[id]);
else add(last[id],i,INF);
last[id] = i;
}
int b;cin>>b;
add(i,T,b);
}
cout<<dinic()<<endl;
return 0;
}
标签:cur,idx,int,ne,2237,猪舍,顾客,AcWing 来源: https://www.cnblogs.com/aitejiu/p/16450269.html