zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    mysql数据库常用指令
    解决windows的mysql无法启动 服务没有报告任何错误的经验。
    “Can't open file for writing”或“operation not permitted”的解决办法
    启动Apache出现错误Port 80 in use by "Unable to open process" with PID 4!
    如何打开windows的服务services.msc
    常见的HTTP状态码 404 500 301 200
    linux系统常用的重启、关机指令
    (wifi)wifi移植之命令行调试driver和supplicant
    linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )
    alloc_chrdev_region申请一个动态主设备号,并申请一系列次设备号
  • 原文地址:https://www.cnblogs.com/chdy/p/12620567.html
Copyright © 2011-2022 走看看