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;
}
posted @ 2021-02-18 17:01  QuantAsk  阅读(39)  评论(0编辑  收藏  举报