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


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

    (n,mleq 100,kleq 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;
    }
    
  • 相关阅读:
    数仓备机DN重建:快速修复你的数仓DN单点故障
    深度学习分类任务常用评估指标
    云小课 | MRS基础入门之HDFS组件介绍
    华为云数据库GaussDB(for Cassandra)揭秘第二期:内存异常增长的排查经历
    为什么vacuum后表还是继续膨胀?
    Go 自定义日志库
    Go time包
    Go 文件操作
    Go 包相关
    【程序人生】跟小伙伴们聊聊我有趣的大学生活和我那两个好基友!
  • 原文地址:https://www.cnblogs.com/chdy/p/12620567.html
Copyright © 2020-2023  润新知