1. 程式人生 > 實用技巧 >【LOJ6030】「雅禮集訓 2017 Day1」矩陣(水題)

【LOJ6030】「雅禮集訓 2017 Day1」矩陣(水題)

點此看題面

  • 給定一個\(n\times n\)的黑白矩陣。
  • 一次操作可以用某一行的格子去覆蓋某一列的格子。
  • 問至少多少步能夠把整個矩陣染黑。
  • \(n\le1000\)

解題思路

先判無解,當且僅當所有格子為白。

考慮我們必然是先集中力量把某一行全部染黑,然後再用這一行把所有不是全黑的列染黑,整體分成兩部分。

列舉選擇第\(i\)行,如果一開始原本就有某一行的第\(i\)列為黑,我們可以利用這一行把所有第\(i\)行為白的列的第\(i\)行都染黑;如果沒有,我們任選一個原本有黑的行去覆蓋第\(i\)列,必然能產生第\(i\)列為黑的行。兩種情況實際上只差一步。

然後我們發現一開始就全黑的列肯定不會去修改,而一開始不是全黑的列我們在之前的操作中肯定不可能把它染全黑,因此這部分的總操作次數實際上是固定的。

然後兩部分拼起來就好了,求解答案的過程複雜度實際上只有\(O(n)\)

程式碼:\(O(n^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
using namespace std;
int n,c[N+5],p[N+5];char s[N+5][N+5];
int main()
{
	RI i,j,t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%s",s[i]+1);
	for(i=1;i<=n;++i) for(j=1;j<=n;++j) s[i][j]=='#'&&(t=1);if(!t) return puts("-1"),0;//如果全白
	for(i=1;i<=n;++i) for(j=1;j<=n;++j) s[i][j]=='#'&&(++c[i],p[j]=1);//統計每行原本黑格子數,每列是否有黑格子
	for(t=0,j=1;j<=n;++j) {for(i=1;i<=n&&s[i][j]=='#';++i);i>n&&++t;}//統計原本全黑的列數
	RI w=1e9;for(i=1;i<=n;++i) w=min(w,!p[i]+(n-c[i])+(n-t));return printf("%d\n",w),0;//列舉染全黑的行統計答案
}