YbtOJ-大收藏家【分层图,最大流】
正题
题目链接:https://www.ybtoj.com.cn/contest/117/problem/2
题目大意
\(n\)个人,每人有\(a_i\)个属于自己的物品。\(m\)次交换依次进行,每次\(x_i,y_i\)两个人可以决定拿不拿自己的一个物品进行交换。
求\(1\)号人最后能拿到最多多少种物品
\(1\leq n,m,a_i\leq 3000\)
解题思路
每种物品只需要一个,所以每种物品的第一个可以视为流量,\(a_i\)可以视为自己的物品处的空位(自己的物品可以不视为自己的)。
\(x_i,y_i\)的交换可以视为一条流量为\(1\)的双向边,因为依次进行所以要分成\(m\)层,然后每一层有交换的连边。
发现这样有很多点没有用到,去掉这些多余的,那点数就是\(O(n+m)\)级别的了
跑最大流就好了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=12100,inf=1e9;
struct node{
int to,next,w;
}a[N<<2];
int T,n,m,tot,cnt,ans,s,t;
int ls[N],dep[N],p[N],w[N];
queue<int> q;
void addl(int x,int y,int w){
a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;
a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;
return;
}
bool bfs(){
memset(dep,0,sizeof(dep));dep[s]=1;
while(!q.empty())q.pop();q.push(s);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(dep[y]||!a[i].w)continue;
dep[y]=dep[x]+1;
if(y==t)return 1;
q.push(y);
}
}
return 0;
}
int dinic(int x,int flow){
if(x==t)return flow;
int rest=0,k;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(dep[x]+1!=dep[y]||!a[i].w)continue;
rest+=(k=dinic(y,min(a[i].w,flow-rest)));
a[i].w-=k;a[i^1].w+=k;
if(rest==flow)return rest;
}
if(!rest)dep[x]=0;
return rest;
}
int main()
{
freopen("collection.in","r",stdin);
freopen("collection.out","w",stdout);
scanf("%d",&T);
while(T--){
tot=0;memset(ls,0,sizeof(ls));
scanf("%d%d",&n,&m);
s=tot=1;t=cnt=2;ans=0;
for(int i=1;i<=n;i++){
p[i]=++cnt;
scanf("%d",&w[i]);
addl(s,p[i],1);
}
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
++cnt;addl(p[x],cnt,w[x]);p[x]=cnt;
++cnt;addl(p[y],cnt,w[y]);p[y]=cnt;
addl(p[x],p[y],1);addl(p[y],p[x],1);
}
addl(p[1],t,inf);
while(bfs())
ans+=dinic(s,inf);
printf("%d\n",ans);
}
return 0;
}