일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 크루스칼
- 브루트포스
- 에라토스테네스의 체
- 플로이드와샬
- 세그먼트트리
- 구현
- 이분탐색
- 이진탐색
- 좌표압축
- 누적합
- 삼분탐색
- 수학
- 비트마스크
- 이분매칭
- DP
- boj
- 정렬
- MST
- lca
- dfs
- lis
- 펜윅트리
- 그리디
- 다익스트라
- LazyPropagation
- 백준
- 투포인터
- DisjointSet
- 위상정렬
- BFS
Archives
- Today
- Total
lastknight00
[백준]컨닝(1014) 본문
문제 링크 : [백준]컨닝(1014)
문제 설명
N * M 의 격자가 주어지고, 각 칸에는 학생이 앉을 수 있는 경우(.)와 앉을 수 없는 경우(x)가 주어집니다.
이때, 모든 학생이 자신과 인접한 왼쪽, 오른쪽, 오른쪽 위 대각선, 왼쪽 위 대각선에 자리를 비우도록 하여 최대한 많은 사람을 앉게 하는 경우, 몇 명까지 앉힐 수 있는지 구하세요.
입력
T(테스트 케이스의 수)
N(격자의 세로 크기, 1 <= N <= 10)
M(격자의 가로 크기, 1 <= M <= 10)
격자 정보
4
2 3
...
...
2 3
x.x
xxx
2 3
x.x
x.x
10 10
....x.....
..........
..........
..x.......
..........
x...x.x...
.........x
...x......
........x.
.x...x....
출력
4
1
2
46
카테고리
#DP #비트마스크
시간 복잡도 상한
매우 커도 될 것 같아요??(O((M*M)!)만 아니면 될 듯??)
해결 방법
- 당연히 모든 경우를 다 돌려보면 답은 구할 수 있으나, 2100이라서 타임아웃이 나올거예요.
- 그러면 중복이 없는지를 살펴봅시다.
- 윗줄의 케이스가 완성이 된다면, 그에 따른 아랫줄의 최댓값은 변하지 않을거예요.
- 따라서 몇번째 줄인지와 윗줄의 상태값을 메모이제이션하여 시간복잡도를 줄일 수 있습니다.
- 그러면 윗줄의 상태는 어떻게 저장을 할지 고민해봅니다.
- 각각의 칸은 앉거나 앉지 않거나 두가지 상태로 구분이 되고, 이런 경우 비트마스크를 사용하여 각 칸의 상태를 1bit만 사용하여 저장할 수 있습니다.
- 그러면 DP 배열의 칸수는 줄 수 10, 상태 1<<10=1,024 즉 10,240칸이 됩니다.
- 각 칸을 채우는데, 최대 1,024가지 경우를 살펴보아야 하니, 대략 10,000,000번의 연산 안에 끝날 수 있습니다.(실제로는 비트 사용 여부에 따라 훨씬 줄어 들 것 같습니다.)
- 한 줄에 대한 모든 상태를 만들어 보고, 그 상태를 다음줄을 확인 할 때 넘겨줍니다.
- 다음 줄에 대한 확인을 하면서, 앉을 수 있는 자리라면, 자신의 왼쪽, 왼쪽 위, 오른쪽 위를 살펴보아 앉을 수 있는지 여부를 확인합니다.
- 오른쪽은 확인 안합니다.(사실 왼쪽부터 살펴보니 확인할 필요도 없거니와, 왼쪽에서 오른쪽을 검사하나 오른쪽에서 왼쪽을 검사하나 동일한 로직이기 때문에 하나만 처리하면 됩니다.)
- 기저 조건을 만나면, 마지막 줄에서 몇 자리에 사람을 앉혔는지 반환합니다.
- 켜진 비트 수를 세면 됩니다.
- 끝까지 올라가면서 모든 경우에 몇명이 앉았는지 확인합니다.
시간복잡도
대략 천만??
코드
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int t,n,m,i,d[10][1<<10];
bool e[10][1<<10];
char s[10][11];
int r(int p,int t);
int u(int p,int t,int z,int x){
int a=0;
if(x==m){
int k=0,q=z;
while(q){
k+=(q&1);
q>>=1;
}
return r(p+1,z)+k;
}
bool lu=p&&x&&s[p-1][x-1]=='.'&&(t&(1<<(x-1)));
bool ru=p&&x<m-1&&s[p-1][x+1]=='.'&&(t&(1<<(x+1)));
bool ll=x&&s[p][x-1]=='.'&&(z&(1<<(x-1)));
if(s[p][x]=='.'&&!lu&&!ru&&!ll)a=u(p,t,z|(1<<x),x+1);
return max(a,u(p,t,z,x+1));
}
int r(int p,int t){
int&a=d[p][t];
if(p==n)return 0;
if(e[p][t])return a;
e[p][t]=1;
return a=u(p,t,0,0);
}
int main(){
scanf("%d",&t);
while(t--){
memset(d,0,sizeof(d));
memset(e,0,sizeof(e));
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)scanf("%s",s[i]);
printf("%d\n",r(0,0));
}
}
'PS' 카테고리의 다른 글
[백준]퀘스트 중인 모험가(15816) (0) | 2020.09.05 |
---|---|
[백준]여러 직사각형의 전체 면적 구하기(2672) (0) | 2020.09.04 |
[백준]하늘에서 떨어지는 1, 2, ..., R-L+1개의 별(17353) (0) | 2020.09.01 |
[백준]괄호 문자열과 쿼리(17407) (0) | 2020.09.01 |
[백준]가로 블록 쌓기(18407) (0) | 2020.08.30 |
Comments