2、刷颜色(brush.cpp)
【题目描述】
我们有一个 H 行 W 列的矩阵方格(1≤H,W≤6)行号是从上往下编号,列号是从左往右
编号。
第 i 行第 j 列位置小方格的颜色用字符 Ci,j
给出(1≤i≤H;1≤j≤W;Ci,j
为
’
.
’
或
’
#
’
):
⚫ 如果 Ci,j为字符
’
.
’
,则该小方格为白色;
⚫ 如果 C
i,j
为字符
’
#
’
,则该小方格为黑色;
考虑执行以下操作:
•选择多行(也可能不选行)和多列(也可能不选列),然后将所选行中的所有格子和所
选列中的所有格子都涂成红色。
给你一个正整数 K(1≤K≤H×W),问你有多少次不同的选择,会导致每个选择的操作
结果都只剩下 K 个黑色方格?
【输入格式】brush.in
第 1 行输入三个正整数 H、W 和 K。
接下来是一个 H 行、W 列的矩阵,每个位置用 ’
.
’
或
’
#
’
表示相应的颜色。
【输出格式】brush.out
输出一个整数,表示满足条件的行和列的选择次数。
如果输入数据本身就满足条件,并且不能做任何选择(选择了就不满足),则输出 1;如
果无法满足剩下 K 个黑色方格,则输出 0。
【输入输出样例 1】
brush.in brush.out
2 3 2 5
..#
###
【算法分析】
题意:有一张图,“.”表示白色,“#”表示黑色,每次可以将多行和多列涂成红色
(也可不涂),问有多少种方案,使得剩下黑点的个数为 k。
可以用二进制枚举(即状态压缩)来将所以情况枚举出来:
对于一个有 N 个元素的集合,共有 2的N次方
个子集,而对于[0,2^
N
],我们可以用 N 的二进制来表示,对于每一位,如果该位是 1,就表示选择了。
于是我们枚举所有行和列的选择方案,然后再枚举每个方案有多少黑点被涂成了红色:
我们可以先枚举某种选择的情况,然后再去遍历每一个位置,判断该位置是否为黑色
#,如是则刷掉这个黑色(cnt++,cnt 是刷掉的#数量)。
设初始的#数量为 sum,则如果某个枚举的方案出现 sum-cnt=k 的情况,则答案
ans++;
【考点】
状态压缩,模拟。
【代码如下】:
#include<bits/stdc++.h>
using namespace std;
int n,m,k,sum=0,cnt,ans=0;
char a[10][10];
int main(){
cin>>n>>m>>k;
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
cin>>a[i][j];
if(a[i][j]=='#') sum++;
}
}
for(int i=0;i<(1<<n);++i) //枚举行的选择情况
{
for(int j=0;j<(1<<m);++j) //枚列的选择情况
{
cnt=0;
for(int r=0;r<n;++r) //枚举某个位置的行
for(int c=0;c<m;++c) //枚举某个位置的列
if(((i&(1<<r)) || (j&(1<<c))) && a[r][c]=='#') cnt++;
if(sum-cnt==k) ans++; //如果位置(r,c)被行或列选择则,被刷掉
} //剩下 k 个黑子则次方案满足条件
}
cout<<ans<<endl;
return 0;
}
【总结】对于此类看起来数据很小,但又可能出现指数级情况且每种情况只有两种可能,通常使用·状态压缩