- Problem
- [1] Answer Code (18. 10. 13)
- [2] Answer Code (18. 10. 13)
- [3] Answer Code (18. 09. 05)
- [4] Wrong Code (18. 04. 10)
Problem
Problem URL : 보호 필름
[1] Answer Code (18. 10. 13)
#include <iostream>
#include <cstring>
#include <algorithm>
#define p pair<int,int>
using namespace std;
int map[13][20];
int ans;
int row,col,k;
bool chk(){
for(int c=0; c<col; c++){
bool flag = true;
/*
row : 6
k : 3 이라면
체크해야하는 row의 범위는
0 ~ 4이다.
그러므로 row-k+1까지 해줘야한다.
*/
for(int r=0; r<row-k+1; r++){
flag = true;
for(int i=1; i<k; i++){
if(map[r][c] != map[r+i][c]){
flag = false;
break;
}
} // end of for i
/*
column을 기준으로
연속으로 k만큼 성립한다면
flag값은 true이다.
그렇기 때문에
현재 column은 조건을 충족시키기 때문에
더이상 볼 필요가 없기에 탈출한다.
*/
if(flag){
break;
}
} // end of for r
/*
column에 대해
모든 경우를 살폈는데
가능한 경우가 없었다.
즉 그 column에서는 조건을 충족시키지 못했다.
그러면 다음 column을 볼 필요도 없이
조건을 충족시키지 못하기 때문에
false를 return한다.
*/
if(! flag)
return false;
} // end of for c
return true;
}
/*
chk()보다 효율적으로 개선한 함수
chk()같은 경우에는
k=3 일 때
각 row마다 k만큼 연속적인지 체크하고
그 다음 row에 대해서도 반복한다.
그런데 chk2()에서는 각 row는 1번만 체크한다.
*/
bool chk2(){
for(int c=0; c<col; c++){
bool flag = false;
int cnt = 0;
int color = -1;
for(int r=0; r<row; r++) {
int v = map[r][c];
if (v == color) {
cnt++;
}
else {
cnt = 1;
color = v;
}
if (cnt >= k) {
flag = true;
break;
}
} // end of for r
if (!flag) {
return false;
}
} // end of for c
return true;
}
void dfs(int st_row, int cnt){ // st_row : 색을 칠할 Row Index
/* st_row >= now 조건
그냥 Pass할 경우 (= dfs(st_row+1,cnt) )
st_row >= row 조건이 없다면
Runtime Error가 발생한다.
즉 무한 루프를 돌게 된다.
*/
if( cnt == 0 || st_row >= row ){
if(chk())
ans = 1;
return ;
}
int tmp_row[20];
for(int j=0; j<col; j++) tmp_row[j] = map[st_row][j];
dfs(st_row+1,cnt);
for(int j=0; j<col; j++) map[st_row][j] = 0;
dfs(st_row+1,cnt-1);
for(int j=0; j<col; j++) map[st_row][j] = 1;
dfs(st_row+1,cnt-1);
for(int j=0; j<col; j++) map[st_row][j] = tmp_row[j];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int TC;
cin >> TC;
for(int tc=1; tc<=TC; tc++){
memset(map,0,sizeof(map));
ans = 2e9;
cin >> row >> col >> k;
for(int i=0; i<row; i++) for(int j=0; j<col; j++) cin >> map[i][j];
/*
k번은 체크할 필요가 없다.
k번만큼 칠 할 수 있다면
무조건 답이 될 수 있기 때문이다.
그러므로 k-1번까지만 dfs를 돌면서 체크한다.
*/
for(int i=0; i<k; i++){
dfs(0,i);
if( ans == 1 ){
ans = i;
break;
}
}
if(ans == 2e9)
ans = k;
cout << "#" << tc << " " << ans << endl ;
}
return 0;
}
Review
- 다음과 같은 실수를 하였다.
int tmp_row[20];
for(int i=st_row; i<row; i++){ // [1]
for(int j=0; j<col; j++) tmp_row[j] = map[i][j];
dfs(i+1,cnt);
for(int j=0; j<col; j++) map[i][j] = 0;
dfs(i+1,cnt-1);
for(int j=0; j<col; j++) map[i][j] = 1;
dfs(i+1,cnt-1);
for(int j=0; j<col; j++) map[i][j] = tmp_row[j];
}
-
[1]으로 인해 시간 초과를 발생시켰다.
-
예를 들어 2번째 row를 색칠하는 경우를 생각해보자.
-
이 때 i=1일 경우 dfs(i+1,cnt)로 index를 넘겨서 재귀 호출된 함수에서 i=2일 때 색칠하는 경우
-
for이 1번 돌아서 i=2가되고 해당 함수에서 색칠하는 경우
-
이렇게 중복되는 경우가 발생한다.
-
결론적으론 시간 초과를 유발시키게 된다.
그래서 for(int i=st_row; i<row; i++) 코드를 삭제하여 시간 초과 문제를 해결하였다.
[2] Answer Code (18. 10. 13)
#include <iostream>
#include <memory.h>
using namespace std;
#define mod 600000
struct p {
int x, cnt, idx;
};
int arrs[mod][20], D, W, K;
p q[mod];
bool check(int arr[20]) {
int tt = (1 << K) - 1, f;
for (int i = 0; i < W; ++i) {
f = 0;
for (int j = 0; j <= D - K; ++j) {
int temp = arr[i] & (tt << j);
if (temp == (tt << j) || temp == 0) {
f = 1;
break;
}
}
if (!f) return false;
}
return true;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
register int t, a[20], i, j;
register int aidx, qs, qe;
cin >> t;
for (int tc = 1; tc <= t; ++tc) {
memset(a, 0, sizeof(a));
cin >> D >> W >> K;
for (i = 0; i < D; ++i) for (int j = 0; j < W; ++j) {
int temp;
cin >> temp;
a[j] = (a[j] << 1) | temp;
}
for (i = 0; i < W; ++i) arrs[0][i] = a[i];
qs = qe = aidx = 1;
cout << "#" << tc << " ";
if (check(arrs[0])) {
cout << "0\n";
}
else {
int maxp = 1 << D, f = 1, ans = K;
for (i = 1; i < maxp; i <<= 1) {
int nx = i;
for (j = 0; j < W; ++j) arrs[aidx][j] = a[j] & ~i;
if (check(arrs[aidx])) {
ans = 1;
break;
}
if (!(nx & 1)) {
q[qe++] = { nx,1,aidx };
aidx++;
}
for (j = 0; j < W; ++j) arrs[aidx][j] = a[j] | i;
if (check(arrs[aidx])) {
ans = 1;
break;
}
if (!(nx & 1)) {
q[qe++] = { nx,1,aidx };
aidx++;
}
}
while (qs<qe && f) {
int x = q[qs].x;
int cnt = q[qs].cnt;
int idx = q[qs++].idx;
if (cnt != K - 1) {
for (i = (x&-x) >> 1; i; i >>= 1) {
int nx = x | i;
for (j = 0; j < W; ++j) arrs[aidx][j] = arrs[idx][j] & ~i;
if (check(arrs[aidx])) {
ans = cnt + 1;
f = 0;
break;
}
if (!(nx & 1)) {
q[qe++] = { nx,cnt + 1,aidx };
aidx++;
}
for (j = 0; j < W; ++j) arrs[aidx][j] = arrs[idx][j] | i;
if (check(arrs[aidx])) {
ans = cnt + 1;
f = 0;
break;
}
if (!(nx & 1)) {
q[qe++] = { nx,cnt + 1,aidx };
aidx++;
}
}
}
else break;
}
cout << ans << '\n';
}
}
return 0;
}
Review
-
Bitmask로 풀이한 코드
-
register 선언은 변수를 레지스터에 저장해서 좀 더 빠른 시간 내 접근이 가능하도록 한다.
[3] Answer Code (18. 09. 05)
#include<iostream>
#include<cstring>
using namespace std;
int ans;
int MAP[13][20];
int r,c,k;
// 세로로 체크하는 로직 정리해두기
bool chkCondition(int (*map)[20]){
for(int i=0; i<c; i++){
bool chk = false;
for(int j=0; j<r; j++){
int continue_cnt = 1;
for(int s=1; j+s<r && s<k; s++){
if(map[j][i] != map[j+s][i]){
break;
}
continue_cnt ++;
} // end of for s
if(continue_cnt == k){
chk = true;
break;
}
} // end of for j
// 해당 Column의 조건을 충족시키지 못했기 때문에 return false;
if(!chk)
return false;
} // end of for i
return true;
}
int dfs(int (*map)[20],int r_idx, int k_cnt){
if( ans < k_cnt ) return ans; // ans 보다 k_cnt가 크면 약을 더 많이 사용한것이기 때문에
if( chkCondition(map) ) return ans = ans > k_cnt ? k_cnt : ans;
if( k_cnt >= k || r_idx >= r) {
if( chkCondition(map) )
return ans = ans > k_cnt ? k_cnt : ans;
else
return 2e9;
}
int origin_map[13][20];
for(int i=0; i<c; i++) origin_map[r_idx][i] = map[r_idx][i];
// 해당 Row를 0으로 초기화
for(int i=0; i<c; i++) map[r_idx][i] = 0;
dfs(map,r_idx+1,k_cnt+1);
// 해당 Row를 1으로 초기화
for(int i=0; i<c; i++) map[r_idx][i] = 1;
dfs(map,r_idx+1,k_cnt+1);
// 해당 Row에 칠하지 않고 Pass
for(int i=0; i<c; i++) map[r_idx][i] = origin_map[r_idx][i];
dfs(map,r_idx+1,k_cnt);
return ans;
}
int main(){
int TC;
cin >> TC;
for (int tc=1; tc<=TC; tc++) {
ans = 2e9;
memset(MAP,0,sizeof(MAP));
cin >> r >> c >> k;
for(int i=0; i<r; i++) for(int j=0; j<c; j++) scanf("%d",&MAP[i][j]);
dfs(MAP,0,0);
printf("#%d %d\n",tc, ans);
} // end of for tc
return 0;
}
Review
- 1시간정도 소요
큰 로직을 짜는데는 금방 짰는데 세로로 조건을 체크하는 부분과 dfs 종료 조건문 로직 짜는데 시간이 걸렸다.
[4] Wrong Code (18. 04. 10)
#include<iostream>
#include<cstring>
using namespace std;
int r,c,k;
int map[14][21];
bool dfs(int map[14][21], int initLine, int depth){
int _map[14][21];
if( depth == 0){ // [1]
for(int a=1; a<=r; a++) for(int b=1; b<=c; b++) _map[a][b] = map[a][b];
// 열마다 k만큼 연결되는지 체크
bool chk = true;
for(int a=1; a<=c && chk; a++){
int cnt = 1;
for(int b=1; b<=r-1; b++){
if( _map[b][a] == _map[b+1][a]){
cnt ++ ;
if( cnt == k)
break;
}
else
cnt = 1;
}
if( cnt != k )
chk = false;
}
return chk;
} // End of [1]
for(int i=initLine; i<=r; i++){ // [2]
for(int a=1; a<=r; a++) for(int b=1; b<=c; b++) _map[a][b] = map[a][b];
for(int j=1; j<=c; j++) _map[i][j] = 0;
if(dfs(_map, i+1, depth-1))
return true;
for(int j=1; j<=c; j++) _map[i][j] = 1;
if(dfs(_map, i+1, depth-1))
return true;
} // End of [2]
return false;
}
int main(){
int tc;
cin >> tc;
// c : 세로
// r : 가로
for(int i=1; i<=tc; i++){ // [0]
memset(map, 0, sizeof(map));
cin >> r >> c >> k;
for(int a=1; a<=r; a++)
for(int b=1; b<=c; b++)
scanf("%d",&map[a][b]);
int _min = k;
for(int v=0; v<=k && v <= _min; v++)
for(int j=1; j<=c; j++)
if(dfs(map,j,v)){
_min = v;
break;
}
printf("#%d %d\n", i, _min);
} // End of [0] Loop
return 0;
}
Review
-
2차원 배열 넘기는걸 못해서 Hard Coding했다.
-
처음에 접근했던 방식이 틀려서 시간이 오래걸렸다.
-
코드를 새로 짜고 채점한 결과 48/50
TC 2개에서 시간초과가 뜬다. 이유를 모르겠다 ㅠㅠ