자료구조&알고리즘
SWEA 3814 평등주의
초코쨔응
2019. 10. 1. 16:08
FAIL한 최초 코드
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
// 사용가능한 라이브러리 여부 확인
/*
3814 평등주의
input)
2
5 5
4 2 3 7 6
5 4
4 2 3 7 8
output)
#1 1
#2 2
문제 발견
5 3
7 3 4 5 6
하면 1이 나와야하는데 2가 나옴
2019.10.01
*/
int main()
{
// unsafe error
// https://bymakers.tistory.com/6
freopen("./input.txt", "r", stdin);
// input
int T, N, K;
int t = 0;
int i = 0;
int start = 0;
int end = 0;
int middle = 0;
int last_middle = 0;
int temp_k = 0;
scanf_s("%d", &T);
for (t = 0; t < T; t++)
{
scanf_s("%d %d", &N, &K);
// int numbers[N]; // 고정 크기의 배열일 때 사용
int *numbers = (int*)malloc(sizeof(int)*N); // 동적 할당 (int*) 을 붙이는 것과 안 붙이는 것의 차이?
int* numbers_cp = (int*)malloc(sizeof(int) * N);
// null 포인터를 역참조하고 있다는 에러 메세지 뜸 C6011
// https://pang2h.tistory.com/196
if (numbers != NULL)
{
for (i = 0; i < N; i++)
{
scanf_s("%d", &numbers[i]);
}
}
int max_value = 0;
// inital value
for (i = 0; i < N-1; i++)
{
if ((numbers[i] - numbers[i+1]) >= max_value)
{
max_value = (numbers[i]-numbers[i+1]);
}
else if ((numbers[i+1] - numbers[i]) >= max_value)
{
max_value = (numbers[i+1] - numbers[i]);
}
}
end = max_value;
middle = (start + end) / 2 + (start + end) % 2;
// main (binary search)
while (1)
{
// test용
//printf("%d %d %d %d %d\n", numbers_cp[0], numbers_cp[1], numbers_cp[2], numbers_cp[3], numbers_cp[4]);
// 돌 때마다 배열 복사 (이분 탐색 할 때마다 원래 배열 상태로 시작)
for (i = 0; i < N; i++)
{
numbers_cp[i] = numbers[i];
}
temp_k = K;
last_middle = middle;
// 돌면서 middle에 해당할 때까지 다 뺀다
for (i = 0; i < N-1; i++)
{
if ((numbers_cp[i] - numbers_cp[i+1]) > middle)
{
temp_k -= ((numbers_cp[i] - numbers_cp[i + 1]) - middle);
numbers_cp[i] -= ((numbers_cp[i] - numbers_cp[i + 1]) - middle);
}
else if ((numbers_cp[i+1] - numbers_cp[i]) > middle)
{
temp_k -= ((numbers_cp[i+1] - numbers_cp[i]) - middle);
numbers_cp[i+1] -= ((numbers_cp[i+1] - numbers_cp[i]) - middle);
}
}
// 차이값을 더 크게 잡음 (이 범위에는 답이 없다)
if (temp_k < 0)
{
start = middle;
middle = (start + end) / 2 + (start + end) % 2;
}
// 차이값을 더 줄여도 됨!
else
{
end = middle;
middle = (start + end) / 2 + (start + end) % 2;
if (middle == last_middle)
{
//max_value = middle;
break;
}
}
}
// output
printf("#%d %d\n", t+1, last_middle);
}
/*
동적 할당
https://mrsnake.tistory.com/45
*/
return 0;
}
성공 코드와 FAIL 코드 비교
- 원래의 실패 코드는 한 번에 오른쪽과 내쪽을 비교하면서 for문을 돌고 끝내버린다. 그러면 맨 처음 요소가 단 한 번밖에 고려되지 않는다. 맨 앞을 다시 보지 않으면 원하는 차이값이 나오기 전에 K가 남았다는 사실을 인지하자마자 가능하다고 가정하고 끝내버린다! K가 부족할 수 있음. 이것을 확인하는 반례는
1
3 7
5 10 2
이다. 잘못 짠 코드는 1이 나오고 제대로 수정하면 2가 나온다.
- 성공 코드에서도 오른쪽으로 확인하는 for문에서 왼쪽 element를 감소시키고, 왼쪽으로 확인하는 다음 for문에서 오른쪽 element를 감소시키면 기본 예제인 4 2 3 7 6 만 비교해봐도 숫자를 제대로 감소시키지 않고 지나간다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int pass_code(int N, int K, int* numbers)
{
int numbers_cp[100000];
int start = 0;
int end = 0;
int middle = 0;
int last_middle = 0;
int temp_k = 0;
int i = 0;
int max_value = 0;
// inital value
for (i = 0; i < N - 1; i++)
{
if ((numbers[i] - numbers[i + 1]) >= max_value)
{
max_value = (numbers[i] - numbers[i + 1]);
}
else if ((numbers[i + 1] - numbers[i]) >= max_value)
{
max_value = (numbers[i + 1] - numbers[i]);
}
}
start = 0; // initialize
end = max_value;
middle = (start + end) / 2;
last_middle = middle; // initialize
// main (binary search)
while (start <= end)
{
// test용
//printf("%d %d %d %d %d\n", numbers_cp[0], numbers_cp[1], numbers_cp[2], numbers_cp[3], numbers_cp[4]);
// 돌 때마다 배열 복사 (이분 탐색 할 때마다 원래 배열 상태로 시작)
for (i = 0; i < N; i++)
{
numbers_cp[i] = numbers[i];
}
temp_k = K;
middle = (start + end) / 2;
// 돌면서 middle에 해당할 때까지 다 뺀다
for (i = 0; i < N - 1; i++)
{
if ((numbers_cp[i + 1] - numbers_cp[i]) > middle)
{
temp_k -= ((numbers_cp[i + 1] - numbers_cp[i]) - middle);
numbers_cp[i + 1] -= ((numbers_cp[i + 1] - numbers_cp[i]) - middle);
}
if (temp_k < 0)
{
break;
}
}
for (i = N - 1; i > 0; i--)
{
if ((numbers_cp[i - 1] - numbers_cp[i]) > middle)
{
temp_k -= ((numbers_cp[i - 1] - numbers_cp[i]) - middle);
numbers_cp[i - 1] -= ((numbers_cp[i - 1] - numbers_cp[i]) - middle);
}
if (temp_k < 0)
{
break;
}
}
if (temp_k < 0)
{
start = middle + 1;
}
// 차이값을 더 줄여도 됨!
else
{
last_middle = middle;
end = middle - 1;
}
}
return last_middle;
}
int fail_code(int N, int K, int* numbers)
{
int numbers_cp[100000];
int start = 0;
int end = 0;
int middle = 0;
int last_middle = 0;
int temp_k = 0;
int i = 0;
int max_value = 0;
// inital value
for (i = 0; i < N - 1; i++)
{
if ((numbers[i] - numbers[i + 1]) >= max_value)
{
max_value = (numbers[i] - numbers[i + 1]);
}
else if ((numbers[i + 1] - numbers[i]) >= max_value)
{
max_value = (numbers[i + 1] - numbers[i]);
}
}
start = 0;
end = max_value;
middle = (start + end) / 2 + (start + end) % 2;
last_middle = middle;
// main (binary search)
while (1)
{
// test용
//printf("%d %d %d %d %d\n", numbers_cp[0], numbers_cp[1], numbers_cp[2], numbers_cp[3], numbers_cp[4]);
// 돌 때마다 배열 복사 (이분 탐색 할 때마다 원래 배열 상태로 시작)
for (i = 0; i < N; i++)
{
numbers_cp[i] = numbers[i];
}
temp_k = K;
// 돌면서 middle에 해당할 때까지 다 뺀다
for (i = 0; i < N - 1; i++)
{
if ((numbers_cp[i] - numbers_cp[i + 1]) > middle)
{
temp_k -= ((numbers_cp[i] - numbers_cp[i + 1]) - middle);
numbers_cp[i] -= ((numbers_cp[i] - numbers_cp[i + 1]) - middle);
}
else if ((numbers_cp[i + 1] - numbers_cp[i]) > middle)
{
temp_k -= ((numbers_cp[i + 1] - numbers_cp[i]) - middle);
numbers_cp[i + 1] -= ((numbers_cp[i + 1] - numbers_cp[i]) - middle);
}
}
// k 범위를 넉넉하게 줘서 기준을 약하게
if (temp_k < 0)
{
start = middle + 1;
middle = (start + end) / 2 + (start + end) % 2;
}
// k 범위를 줄여서 기준을 강하게!
else
{
last_middle = middle;
end = middle - 1;
middle = (start + end) / 2 + (start + end) % 2;
if (start > end)
{
break;
}
}
}
return last_middle;
}
int main(void)
{
// generate test case
int test_case;
int T = 200;
int N, K;
srand((unsigned)time(NULL));
for (test_case = 1; test_case <= T; ++test_case)
{
// 생성
N = rand();
K = rand();
printf("N: %d K: %d ", N, K);
// input
int i = 0;
int start = 0;
int end = 0;
int middle = 0;
int last_middle = 0;
int temp_k = 0;
// int numbers[N]; // 고정 크기의 배열일 때 사용
int numbers[100000];
//int* numbers = (int*)malloc(sizeof(int) * N); // 동적 할당 (int*) 을 붙이는 것과 안 붙이는 것의 차이?
//int* numbers_cp = (int*)malloc(sizeof(int) * N);
// null 포인터를 역참조하고 있다는 에러 메세지 뜸 C6011
// https://pang2h.tistory.com/196
if (numbers != NULL)
{
for (i = 0; i < N; i++)
{
numbers[i] = rand();
// printf("%d ", numbers[i]);
}
}
int max_value = 0;
int pass_ans = pass_code(N, K, numbers);
int fail_ans = fail_code(N, K, numbers);
// output
if (pass_ans == fail_ans) printf("#%d True\n", test_case);
else
{
printf("#%d False: pass ans %d fail ans %d", test_case, pass_ans, fail_ans);
}
}
return 0; //정상종료시 반드시 0을 리턴해야 합니다.
}
문제가 있지만 통과된 코드
- lo = 0; 초기화를 해야한다. 지금 차이값들보다 더 작아질 수 있기 때문에
- return false; for문 가장 마지막으로 내려서 K값을 만족한 것이 맞는지 마지막 요소까지 포함해서 확인을 해야한다.
#include <iostream>
using namespace std;
#define MAX 1000000007
#define MIN -1
int tc, T, N, K, A[100001], _A[100001];
int ABS(int v) { return v > 0 ? v : -v; }
bool conf(int c) {
int cnt = 0;
for (int a = 0; a < N; a++) _A[a] = A[a];
for (int a = 0; a < N - 1; a++) {
if (cnt > K) return false;
if (_A[a] + c < _A[a + 1]) {
cnt += (_A[a + 1] - (_A[a] + c));
_A[a + 1] = _A[a] + c;
}
}
for (int a = N - 1; a > 0; a--) {
if (cnt > K) return false;
if (_A[a] + c < _A[a - 1]) {
cnt += (_A[a - 1] - (_A[a] + c));
_A[a - 1] = _A[a] + c;
}
}
for (int a = 0; a < N - 1; a++) {
if (cnt > K) return false;
if (_A[a] + c < _A[a + 1]) {
cnt += (_A[a + 1] - (_A[a] + c));
_A[a + 1] = _A[a] + c;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> T;
for (tc = 1; tc <= T; tc++) {
cin >> N >> K;
int lo = MAX, hi = MIN;
for (int a = 0; a < N; a++) {
cin >> A[a];
if (a > 0) {
int tmp = ABS(A[a] - A[a - 1]);
if (lo > tmp) lo = tmp;
if (hi < tmp) hi = tmp;
}
}
int ans = -1;
while (lo <= hi) {
int mi = (lo + hi) / 2;
if (conf(mi)) ans = mi, hi = mi - 1;
else lo = mi + 1;
}
cout << "#" << tc << " " << ans << "\n";
}
return 0;
}
문제가 없었던 코드
- 이분탐색 방식에 대한 내용 (https://ilyoan.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%BD%94%EB%94%A9%EA%B8%B0%EB%B2%95-4-%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC%EC%84%9C%EC%B9%98)
#include<iostream>
#define MIN (0)
#define MAX (1000000000)
using namespace std;
int A[100000];
int B[100000];
int K, T, N;
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
freopen("./input.txt", "r", stdin);
cin >> T;
for (int test = 1; test <= T; ++test) {
cin >> N >> K;
for (int i = 0; i < N; ++i) {
int tmp; cin >> tmp;
A[i] = tmp;
}
int min = MIN, max = MAX;
int ans = 0;
while (min < max) {
int k = K;
int mid = (min + max) / 2;
bool check = true;
for (int i = 0; i < N; ++i) B[i] = A[i];
for (int i = 0; i < N - 1; ++i) {
int diff = B[i + 1] - B[i];
if (diff > mid) {
B[i + 1] -= diff - mid;
k -= (diff - mid);
if (k < 0) {
check = false;
break;
}
}
}
for (int i = N - 1; i > 0; --i) {
int diff = B[i - 1] - B[i];
if (diff > mid) {
B[i - 1] -= (diff - mid);
k -= (diff - mid);
if (k < 0) {
check = false;
break;
}
}
}
if (!check) {
min = mid + 1;
ans = min;
}
else {
max = mid;
}
}
cout << "#" << test << " " << ans << '\n';
}
return 0;
}
d
반응형