将每一行和每一列分别作为一个点,当第$i$行第$j$列的格子为红色时,将第$i$行与第$j$列连边
此时,考虑选择第$i$行的红色格子并将第$i$行的格子全部改成白色:
关于这一操作的条件,即需要第$i$行有红色格子,从图中来看也即第$i$行对应的点度非0
关于这一条件的影响,即第$i$行的红色格子都没了,从图中来看也即删去第$i$行对应的点所有出边
根据上述分析,每一次操作即在图中选择一个度非0的点,并删除其所有出边,并且设最终选择了$x$个行对应的点和$y$个列对应的点,最大化$nm-(n-x)(m-y)$
考虑其中的一个连通块(连通块之间显然是独立的),我们可以仅保留这个连通块中的任意一个点,并选择该连通块中其余的点
具体来说,任取该连通块的一个生成树,并以需要保留的点为根,每一次不断选择一个叶子即可
另一方面,显然无论如何我们都不可能删除一个连通块中的所有点,因此这已经最优了
同时,我们仅关心每一个连通块中未选的点是行还是列,一个点的连通块显然不需要考虑,多个点的连通块必然同时含有行和列的点
由此,求出所有连通块(不考虑一个点的连通块)的数量$k$以及$x-1$和$y-1$之和$X$和$Y$(其中$x$和$y$为该连通块中行和列对应的点数量),问题即求$f(x)=nm-(n-X-x)(m-Y-k+x)$在$xin [0,k]$的最大值,利用二次函数简单讨论即可
方案根据上面的分析也不难构造,时间复杂度为$o(nm)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2505 4 struct Edge{ 5 int nex,to; 6 }edge[N*N*2]; 7 struct Op{ 8 int type,x,y; 9 }; 10 vector<int>v[N<<1]; 11 vector<Op>ans; 12 int E,n,m,x,y,scc,head[N<<1],vis[N<<1]; 13 char s[N][N]; 14 void add(int x,int y){ 15 edge[E].nex=head[x]; 16 edge[E].to=y; 17 head[x]=E++; 18 } 19 void dfs(int k){ 20 if (vis[k])return; 21 vis[k]=1; 22 v[scc].push_back(k); 23 for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to); 24 } 25 void dfs(int k,int fa){ 26 if (vis[k])return; 27 vis[k]=1; 28 for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to,k); 29 if (fa>=0){ 30 if (k<n)ans.push_back(Op{0,k,fa-n}); 31 else ans.push_back(Op{1,fa,k-n}); 32 } 33 } 34 void write(){ 35 printf("%d ",ans.size()); 36 for(int i=0;i<ans.size();i++){ 37 ans[i].x++,ans[i].y++; 38 if (ans[i].type==0)printf("X %d %d ",ans[i].x,ans[i].y); 39 else printf("Y %d %d ",ans[i].x,ans[i].y); 40 } 41 } 42 int main(){ 43 scanf("%d%d",&n,&m); 44 memset(head,-1,sizeof(head)); 45 for(int i=0;i<n;i++){ 46 scanf("%s",s[i]); 47 for(int j=0;j<m;j++) 48 if (s[i][j]=='R'){ 49 add(i,j+n); 50 add(j+n,i); 51 } 52 } 53 for(int i=0;i<n+m;i++) 54 if (!vis[i]){ 55 v[++scc].clear(); 56 dfs(i); 57 if (v[scc].size()==1)scc--; 58 else{ 59 x--,y--; 60 for(int j=0;j<v[scc].size();j++){ 61 if (v[scc][j]<n)x++; 62 else y++; 63 } 64 } 65 } 66 memset(vis,0,sizeof(vis)); 67 if (m-n+x-y<0){ 68 for(int i=1;i<=scc;i++) 69 for(int j=0;j<v[i].size();j++) 70 if (v[i][j]<n){ 71 dfs(v[i][j],-1); 72 break; 73 } 74 write(); 75 } 76 else{ 77 for(int i=1;i<=scc;i++) 78 for(int j=0;j<v[i].size();j++) 79 if (v[i][j]>=n){ 80 dfs(v[i][j],-1); 81 break; 82 } 83 write(); 84 } 85 }