1. 程式人生 > 實用技巧 >E. Bricks(最少1*x 覆蓋矩陣,最大獨立集)

E. Bricks(最少1*x 覆蓋矩陣,最大獨立集)

題:https://codeforces.com/contest/1404/problem/E

題意:給定n*m矩陣‘#’表示要用磚頭覆蓋,‘.’表示不能被覆蓋,只有1*x的磚頭(x可任意),問在磚頭不相互覆蓋的前提下,最少用幾塊磚頭能按規定把矩陣覆蓋

分析:

  • 假設一開始全部都是1*1的磚頭去覆蓋,那麼就有sum塊(‘#’點的個數),要是相鄰邊合併sum減1,那麼就是求在合法情況下最多能合併多少邊;
  • 在合法情況下就是限制的1*x,也就不能出現‘L’型磚頭,假設要是倆塊磚頭能形成‘L’型,那麼就將這倆塊磚頭連邊(以對偶圖的形式)(這明顯就是一個二分圖的模型);
  • 合法情況就是求獨立集(點集中各點沒有連邊)大小;
  • 而最大獨立集就是二分圖中節點-最小點覆蓋,應用到這道題上就是去掉這些點,相應連的邊也就沒有了,相當於花了貢獻把這些點獨立出來,剩下的點也就相互獨立了;
  • ans=sum-最大獨立集。
  • (下圖示號只是方便,不代表該點的真實點標號)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int inf=0x3f3f3f3f;
const ll INF=(1ll<<40);
const int
M=1e6+5; const int N=250; int tot,S,T,n,m; struct node{ int u,v,nextt; int w; }e[M<<1]; char s[N][N]; int head[M],deep[M],cur[M]; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt
=head[v]; head[v]=tot++; } bool bfs(){ for(int i=0;i<=T;i++) deep[i]=0; queue<int>que; que.push(S); deep[S]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==T) return true; que.push(v); } } } return deep[T]!=0; } int dfs(int u,int fl){ if(u==T) return fl; int ans=0,x=0; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==deep[u]+1){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; ans+=x; if(ans==fl) return ans; if(e[i].w) cur[u]=i; } } if(ans==0) deep[u]=0; return ans; } int dinic(){ int res=0; while(bfs()){ for(int i=0;i<=T;i++) cur[i]=head[i]; res+=dfs(S,inf); } return res; } int getid(int x,int y,int other){ return (x-1)*m+y+other; } int main(){ scanf("%d%d",&n,&m); S=2*n*m+10,T=S+1; for(int i=0;i<=T;i++) head[i]=-1; for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) if(s[i][j]=='#'){ ///將橫縱作為二分圖的兩邊 ///L R在對偶圖上相差1 int L = (j-1>=1&&s[i][j-1]=='#') ? getid(i,j-1,0) : 0; int R = (j+1<=m&&s[i][j+1]=='#') ? getid(i,j-1,1) : 0; ///U D在對偶圖上相差m int U = (i-1>=1&&s[i-1][j]=='#') ? getid(i-1,j,n*m) : 0; int D = (i+1<=n&&s[i+1][j]=='#') ? getid(i-1,j,n*m+m) : 0; if(L&&U) addedge(L,U,1); if(L&&D) addedge(L,D,1); if(R&&U) addedge(R,U,1); if(R&&D) addedge(R,D,1); } } int sum=0; int res=0;///二分圖點數 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(s[i][j]=='#'){ sum++; ///row點連線S if(j+1<=m&&s[i][j+1]=='#') addedge(S,getid(i,j-1,1),1),res++; ///colu點連線T if(i+1<=n&&s[i+1][j]=='#') addedge(getid(i-1,j,n*m+m),T,1),res++; } printf("%d\n",sum-(res-dinic())); return 0; }
View Code