zoukankan      html  css  js  c++  java
  • 「CF1404E」Bricks


    • 分析

      类似的,根据横/纵来构造二分图。

      这题的突破点在砖块不能互相重叠,所以对于一个黑色格子,不能同时放 (2)(1 imes x (x>1)) 的砖块。

      如图:

      正因为如此,放横的就不能放竖的,放竖的就不能放横的。此时一个二分图就成形了。如下图:

      建好图后,要求的结果就可以转换了,道理类似前言的那题,最后要求的是最多选多少点,且这些点之间没有连边(保证结果最小)

      即独立集。

      由引理得出结果,二分图的最大独立集 = 总点数-最大匹配数

      因为这题 (n,m le 200) ,直接跑二分图匹配复杂度似乎会到 (O(n^2m^2)) 。所以本题用网络流跑。


    • 代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int Maxn=8e4+5;
    const int inf=1e9;
    struct edge{
    	int v,nx,w;
    }e[Maxn<<4];
    int n,m,ne=-1,f[Maxn],deep[Maxn];
    int cur[Maxn];
    char str[205][205];
    bool vis[Maxn];
    queue<int> q;
    void read(int u,int v,int w)
    {	
    	e[++ne].v=v;
    	e[ne].nx=f[u];
    	e[ne].w=w;
    	f[u]=ne;
    }
    bool bfs(int s,int t)
    {	
    	memset(deep,0x7f,sizeof(deep));
    	while(!q.empty())q.pop();
    	for(int i=0;i<=t;i++)cur[i]=f[i];
    	deep[s]=0;
    	q.push(s);
    	while(!q.empty())
    	{	
    		int now=q.front();
    		q.pop();
    		for(int i=f[now];i!=-1;i=e[i].nx)
    			if(deep[e[i].v]>inf&&e[i].w)
    			{	
    				deep[e[i].v]=deep[now]+1;
    				q.push(e[i].v);
    			}
    	}
    	if(deep[t]<inf)return 1;
    	return 0;
    }
    int dfs(int now,int t,int limit)
    {	
    	if(!limit||now==t)return limit;
    	int flow=0,x;
    	for(int i=cur[now];i!=-1;i=e[i].nx)
    	{	
    		cur[now]=i;
    		if(deep[e[i].v]==deep[now]+1)
    		{	
    			x=dfs(e[i].v,t,min(limit,e[i].w));
    			if(!x)continue;
    			flow+=x;
    			limit-=x;
    			e[i].w-=x;
    			e[i^1].w+=x;
    			if(!limit)break;
    		}
    	}
    	return flow;
    }
    int dinic(int s,int t)
    {	
    	int maxflow=0;
    	while(bfs(s,t))maxflow+=dfs(s,t,inf);
    	return maxflow;
    }
    int main()
    {	
    	int s,t,sum=0,cnt=0;
    	memset(f,-1,sizeof(f));
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		scanf("%s",str[i]+1);
    	s=0,t=n*m*2+1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{	
    			if(str[i][j]=='.')continue;
    			sum++;
    			int uu=(i-1)*m+j,rr=uu+n*m;// uu:id(i,j) rr:id(i,j)_
    			int vv=i*m+j,ll=rr-1;// vv:id(i+1,j) ll:id(i,j-1)_
    			if(str[i-1][j]=='#'&&!vis[uu])++cnt,vis[uu]++,read(s,uu,1),read(uu,s,0);
    			if(str[i][j-1]=='#'&&!vis[ll])++cnt,vis[ll]++,read(ll,t,1),read(t,ll,0);
    			if(str[i-1][j]=='#')
    			{	
    				if(str[i][j+1]=='#')read(uu,rr,1),read(rr,uu,0);
    				if(str[i][j-1]=='#')read(uu,ll,1),read(ll,uu,0);
    			}
    			if(str[i+1][j]=='#')
    			{	
    				if(str[i][j+1]=='#')read(vv,rr,1),read(rr,vv,0);
    				if(str[i][j-1]=='#')read(vv,ll,1),read(ll,vv,0);
    			}
    		}
    	printf("%d
    ",sum-(cnt-dinic(s,t)));
    	return 0;
    }
    

    [ ext{by Rainy7} ]

  • 相关阅读:
    不用keytool,tomcat打开https
    sqlserver获取某一张表中的所有列中的最大长度
    不用keytool,tomcat打开https
    到底私钥和公钥哪个是用来加密 哪个是用来解密的
    空间支持php解压
    到底私钥和公钥哪个是用来加密 哪个是用来解密的
    sqlserver获取某一张表中的所有列中的最大长度
    数字签名(代码签名)流程
    功夫电影中非常经典(武术非常实用)
    数字签名(代码签名)流程
  • 原文地址:https://www.cnblogs.com/Rainy7/p/solution-CF1404E.html
Copyright © 2011-2022 走看看