题目
题目链接:https://www.ybtoj.com.cn/contest/117/problem/2
(Tleq 10,n,m,a_ileq 3000)。
思路
我们只需要最大化 (1) 最终藏品数量,那么显然所有人的藏品可以看作只有一个黑色,剩余 (a_i-1) 个全部都是没有用的白色。我们需要最大化最后 (1) 号黑色的藏品数量。
考虑如果两个人交换两个黑色的,那么其实等价于没有交换。如果交换两个白色的,那么毫无意义。所以我们肯定只让一个黑色和一个白色进行交换。
所以我们可以把白色看作是格子,也就是任何人任何时刻的物品不能超过 (a_i) 个,每一次的“交换”其实可以看做一个人把自己的物品给另一个人。
这很符合一个网络流模型。把所有人拆成 (m+1) 个点,从源点向所有人第一个点连一条流量为一的边。对于第 (i) 个时刻,每一个人的第 (i) 个点向第 (i+1) 个点连一条流量为 (a_i) 的边,限制最多只能有 (a_i) 个物品。如果 (x) 和 (y) 在第 (i) 时刻交换物品,那么就在 (x,y) 的第 (i) 号点之间连一条流量为 (1) 的双向边。最后 (1) 的第 (m+1) 个点向汇点连边。
但是这样点数是 (O(nm)) 的,不可接受。我们发现每一个时刻只需要连这个时刻有关的人的点就可以了。这样点数就降到了 (O(n+m))。
时间复杂度 (O(Tnm))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=10010,Inf=1e9;
int Q,S,T,n,m,maxf,tot,a[N],U[N],V[N],head[N],last[N],dep[N],cur[N];
struct edge
{
int next,to,flow;
}e[N*10];
void add(int from,int to,int flow)
{
e[++tot]=(edge){head[from],to,flow};
head[from]=tot;
swap(from,to);
e[++tot]=(edge){head[from],to,0};
head[from]=tot;
}
bool bfs()
{
memset(dep,0x3f3f3f3f,sizeof(dep));
memcpy(cur,head,sizeof(cur));
queue<int> q;
q.push(S); dep[S]=0;
while (q.size())
{
int u=q.front(); q.pop();
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (e[i].flow && dep[v]>dep[u]+1)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]<Inf;
}
int dfs(int x,int flow)
{
if (x==T) return flow;
int used=0,res;
for (int i=cur[x];~i;i=e[i].next)
{
int v=e[i].to; cur[x]=i;
if (e[i].flow && dep[v]==dep[x]+1)
{
res=dfs(v,min(e[i].flow,flow-used));
used+=res;
e[i].flow-=res; e[i^1].flow+=res;
if (flow==used) return used;
}
}
return used;
}
void dinic()
{
while (bfs()) maxf+=dfs(S,Inf);
}
int main()
{
freopen("collection.in","r",stdin);
freopen("collection.out","w",stdout);
S=N-1; T=N-2;
scanf("%d",&Q);
while (Q--)
{
memset(head,-1,sizeof(head));
tot=1; maxf=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
last[i]=i; add(S,i,1);
}
for (int i=1;i<=m;i++)
{
scanf("%d%d",&U[i],&V[i]);
add(last[U[i]],n+i*2-1,a[U[i]]);
add(last[V[i]],n+i*2,a[V[i]]);
add(n+i*2-1,n+i*2,1);
add(n+i*2,n+i*2-1,1);
last[U[i]]=n+i*2-1; last[V[i]]=n+i*2;
}
add(last[1],T,a[1]);
dinic();
printf("%d
",maxf);
}
return 0;
}