4.2 省选模拟赛 摆棋子 网络流 最大流/上下界最小流

题意:n×m的棋盘 有k个格子坏了不能放棋子 每个好的格子只能放一个棋子 至少要摆多少个棋子满足行的棋子个数>=\(x_i\)列的棋子个数>=\(y_i\)

\(n,m\leq 100,k\leq nm\)

网络流经典题目 不过好久没做了 还是有点菜。

显然 可以考虑二分 结果发现dp不了 看n,m的范围考虑网络流。

有限制,将行列当成二分的节点 格子相当于连边 可以跑流了 发现源点向每一行的流量>=\(x_i\)汇点对列也是如此。

有上下界的最小流即可。但是这种做法并不推荐 (因为这是强行在套模板 尽管思路简单。

而且这个最小流 有些细节可能容易写挂。譬如 循环流只是在判断合法不合法。

真正从 S->T的流量是 T到S的边的反向边。因为所有的流量都是T到S流的。

最后还需要退流。保证图中的流量在合法的时候最小。

考虑 普通的最大流怎么做 可以发现直接求的是最大的数量 和最小的数量不同。

不难将题意转换成求最大能够去掉的棋子数。

判断合法不合法很容易。考虑先在图中满足所有棋子都在图中。

考虑建图:不难建出 源点向没一行都建出总-限制的流量 对列也是如此。

没有坏掉的格子建边。求出最多能够去掉的棋子个数即可。

可以发现 满足题中的要求 且 求出了去掉棋子最多的个数 总数减一下即可。这也是通常说的正难则反。

code:上下界最小流代码。

const int MAXN=210;
int n,m,k,len=1,SS,TT,S,T,l,r;
int x[MAXN],y[MAXN];
int a[MAXN][MAXN],d[MAXN],q[MAXN*MAXN],vis[MAXN],cur[MAXN];
int lin[MAXN],ver[MAXN*MAXN<<1],nex[MAXN*MAXN<<1],e[MAXN*MAXN<<1];
inline void add(int x,int y,int z)
{
	ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;
	ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0;
}
inline void add(int x,int y,int l,int r)
{
	d[x]-=l;d[y]+=l;
	add(x,y,r-l);
}
inline int bfs(int s1,int s2)
{
	rep(1,TT,i)vis[i]=0,cur[i]=lin[i];
	l=r=0;q[++r]=s1;vis[s1]=1;
	while(++l<=r)
	{
		int x=q[l];
		go(x)
		{
			if(vis[tn]||!e[i])continue;
			vis[tn]=vis[x]+1;
			q[++r]=tn;
			if(tn==s2)return 1;
		}
	}
	return 0;
}
inline int dinic(int x,int s2,int flow)
{
	if(x==s2)return flow;
	int k=0,res=flow;
	for(int i=cur[x];i&&res;i=nex[i])
	{
		cur[x]=i;int tn=ver[i];
		if(vis[tn]==vis[x]+1&&e[i])
		{
			k=dinic(tn,s2,min(e[i],flow));
			if(!k){vis[tn]=0;continue;}
			e[i]-=k;e[i^1]+=k;res-=k;
		}
	}
	return flow-res;
}
int main()
{
	freopen("chessman.in","r",stdin);
	freopen("chessman.out","w",stdout);
	get(n);get(m);get(k);
	S=n+m+1;T=S+1;
	rep(1,n,i)get(x[i]),add(S,i,x[i],INF);
	rep(1,m,j)get(y[j]),add(j+n,T,y[j],INF);
	rep(1,k,i)
	{
		int x,y;
		get(x);get(y);
		a[x][y]=1;
	}
	rep(1,n,i)rep(1,m,j)
	{
		if(a[i][j])continue;
		add(i,j+n,0,1);
	}
	SS=T+1;TT=SS+1;
	int res=0;
	rep(1,T,i)
	{
		if(d[i]>0)add(SS,i,d[i]),res+=d[i];
		if(d[i]<0)add(i,TT,-d[i]);
	}
	int sum=0,flow=0;
	add(T,S,INF);
	while(bfs(SS,TT))while((flow=dinic(SS,TT,INF)))sum+=flow;
	if(sum!=res){puts("No Solution");return 0;}
	sum=0;res=e[len];e[len^1]=0;
	while(bfs(T,S))while((flow=dinic(T,S,INF)))sum+=flow;
	put(res-sum);return 0;
}
posted @ 2020-04-02 16:11  chdy  阅读(172)  评论(0编辑  收藏  举报