1. 제목 링크 :
막 기록
2. 주제의 주요 아이디어 :
중국어 질문 ~~
3. 분석 :
n은 20에 불과하므로 이진 열거 연산의 행을 고려하십시오.
따라서 cnt [i]로 표시되는 행에서 i 연산을 수행 한 후 0 개의 열 수만 전처리하면됩니다.
먼저 열 상태가 i 인 열 수를 찾아서 cnt2 [i]로 기록합니다.
그러면 cnt [i] = sum (cnt2 [i의 하위 집합]).
예를 들어, cnt2 [00], cnt2 [01], cnt2 [10], cnt2 [11] 열 상태의 수를 계산 한 후
cnt [00] = cnt2 [00]
cnt [01] = cnt2 [00] + cnt2 [01]
cnt [10] = cnt2 [00] + cnt2 [10]
cnt [11] = cnt2 [00] + cnt2 [01] + cnt2 [10] + cnt2 [11].
분명히, cnt2 []를 찾은 후, 서브 세트의 접두사 합계를 찾으십시오.
예를 들어 입력은 다음과 같습니다.
1 4 4 2 3 *... *... .*** ..**
그러면 열 상태는 0011, 0100, 1100, 1100입니다.
cnt2 [0011] = 1, cnt2 [0100] = 1, cnt2 [1100] = 2를 누릅니다.
작업이 행 3과 4에서 수행되고 행 작업 상태가 1100이라고 가정합니다.
행 연산 1100 후 어떤 열이 0 열이 될 것인지 고려할 때 m-zero 열의 수는 여전히 필요한 열 피연산자의 수입니다.
행 연산 1100 이후 열 상태가 0000, 0100, 1000, 1100 인 열은 모두 0 열이된다는 것을 쉽게 알 수 있습니다.
따라서 행 연산 (1100) 이후 0 열의 수는 cnt2 [0000] + cnt2 [0100] + cnt2 [1000] + cnt2 [1100]입니다.
즉, 행 연산 i 후 0 개 열의 수는입니다 . 여기서 j는 i의 하위 집합입니다.
4. 코드 구현 :
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = (int)1e5;
const int N = (int)2e1;
char s[N + 5][M + 5];
int cnt[1<<N];
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int n, m, a, b; scanf("%d %d %d %d", &n, &m, &a, &b);
for(int i = 0; i < (1<<n); ++i) cnt[i] = 0;
for(int i = 0; i < n; ++i) scanf("%s", s[i]);
for(int i = 0; i < m; ++i)
{
int state = 0;
for(int j = 0; j < n; ++j) state |= (s[j][i] == '*' ? (1<<j) : 0);
++cnt[state];
}
for(int i = 0; i < n; ++i)//子集前缀和
{
for(int j = 0; j < (1<<n); ++j) if(!(j & (1<<i))) cnt[j | (1<<i)] += cnt[j];
}
bool flag = 0; for(int i = 0; i < (1<<n); ++i) flag |= (__builtin_popcount(i) <= a && m - cnt[i] <= b);
puts(flag ? "yes" : "no");
}
return 0;
}