시험장 생존 마스터 가이드

main.cpp를 처음 열었을 때 3분 해체 → SCORE 판독 → 문제별 공략 → 시간 배분까지. 시험장 0분부터 바로 쓰는 실전 매뉴얼.
7문제 · 6레인 · 역공학 + 휴리스틱 최적화

삼성 SW Expert 신규유형 — 시험장 생존 마스터 가이드

7개 함수제출형 휴리스틱 최적화 문제 역공학 완전 해체본 채점기 main.cpp 7종 전부 직접 분석. 시험장 0분부터 즉시 적용 가능.


Part 0 — "main.cpp 처음 열었을 때" 3분 해체 체크리스트

채점기를 처음 열면 반드시 이 7단계를 순서대로 확인한다. 이것만으로 전략 80%가 결정된다.

0-1. SCORE 방향 (최소 vs 최대) — 10초

main.cpp 맨 끝의 printf("SCORE: %lld\n", SCORE) 줄을 찾는다. 그 위로 올라가 gTotalScore/gTotalCost더해지는지(+), 빼지는지(-) 확인. - 점수가 누적 덧셈 + 품질/만족도 점수최대화 (MAXIMIZE) - 거리/비용이 누적최소화 (MINIMIZE) - SCORE += PENALTY 후 조건 실패시 그대로 두면 → PENALTY가 최악값

0-2. ±PENALTY 패턴 — 30초

grep -n 'PENALTY' 실행. 두 가지 패턴: - 패턴 A (가산 후 차감): gTotalCost += PENALTY; ... gTotalCost -= PENALTY; → 합법 액션은 PENALTY가 상쇄됨. 불법 0건이 1차 목표. (2509 deploy, 2603 verify) - 패턴 B (최종 검증 폭락): if(!verify()){ SCORE = PENALTY; break; } → 마지막에 전체 검증. 하나라도 틀리면 PENALTY 1회 폭락. (2507, 2603)

0-3. 의사난수 구조 — 20초

pseudo_rand() 함수 찾기. 계수 25214903917ULL이면 표준 삼성 LCG. - 시드가 scanf로 입력 → TC마다 결정론적, 같은 시드면 같은 입력. - 시드가 seed = tc + 1 고정 → 완전 예측 가능. - 시드를 풀이가 알 수 있으면 → PRNG shadow 복제로 미래 예측 (2601 핵심 기법).

0-4. 시간 버짓 — 30초

clock() / TIME_LIMIT / current_time += 찾기. - TIME_LIMIT 존재 → 시간 내 최대한 많은 액션 수행이 곧 점수 (2511). - gCurrentTime >= TIME_LAP정확히 N번 호출해야 함 (2601은 1000번). - clock_t 측정 있음 → 5초 wall-clock 제한 (2511, 2507).

0-5. extern API 함수 — 30초

user.cpp에서 extern 선언된 함수들이 내가 호출할 수 있는 액션. - 경로계: move, load, unload, deliver, deploy - 조회계: getRoadInfo, getFreightInfo, feature_info, user_info, getProductList - 반드시 user.cpp의 extern 선언과 main.cpp의 구현 시그니처를 일치시킬 것.

0-6. 입력 데이터 구조 — 30초

struct Cell/Path/Freight/Delivery/Coordinates/Element 정의 확인. 어떤 필드가 최적화 변수(내가 바꾸는 것)이고 어떤 것이 고정 제약(바꾸면 안 되는 것)인지 식별. - 예: 2509 Cell → x,y는 최적화 변수, width,height,netCount,nets는 고정.

0-7. SCORE 계산식 역추적 — 30초

wireLengthCost(), pathTimingCost(), user_satisfaction() 등 비용 함수 찾기. 무엇을 최적화해야 하는지가 여기에 있다. - 배선 길이 × 핀 수 합 (2509) - 인접 원소 가중치 곱 합 (2503) - 만족도 - 전력비용 (2601)


Part 1 — SCORE 방향 3초 판독법

문제 방향 핵심 공식 PENALTY 기본점수 획득 조건
2502 원형화물 ↓ 최소화 (50+10)×(dist+10) + (w+50)×(dist+10) 1e14 모든 화물 배송 완료
2503 단백질폴딩 ↑ 최대화 Σ 인접원소값 곱 (H=1,C=2,O=5,N=10,S=30) 0 (실패시) 겹침 없는 유효 접힘
2507 노후화도로 ↓ 최소화 (10+repair)×totalWeight + (load/unload) 1e12 2000 화물 전부 목적지 도착
2509 RTX5090 ↓ 최소화 wireLengthCost + pathTimingCost 1e11 (±패턴) 모든 셀 합법 배치 + 비겹침
2511 배달기사 ↑ 최대화 3000 + 300×(dest-src) per deliver 시간초과시 0점 TIME_LIMIT 내 최대 deliver
2601 TV화질 ↑ 최대화 Σ(sat) - powerUsed×0.01 per step -1 (초과), 1 (전력초과) 1000번 screen_control + 예산내
2603 물류배송2 ↓ 최소화 move거리 + weight×100 (load/unload) 1e11 10000 고객 전부 정확한 상품

±PENALTY 판독 핵심

  • 2509: deploy()에서 gTotalCost += PENALTY 후 성공시 -= PENALTY. deploy 안 하면 PENALTY 그대로. 최소 deploy 한 번씩은 해야 PENALTY 차감.
  • 2603: verify()가 모든 고객이 정확히 1개 정확한 상품을 가질 때만 true. 하나라도 틀리면 1e11 폭락.
  • 2503: verify()가 false면 gTotalScore = 0. 겹침 하나라도 있으면 0점.

Part 2 — 문제별 해체 시트

2502 원형화물배치 (transCircle)

ANATOMY 요약 - ⚠️ 채점기 없음 (main.cpp 미확보). 풀이 user.cpp만 존재. - API: move(mDistance), load(mCargoID), unload(mCargoID) - MAPSIZE=1000 원형 경로, MAX_TC=10, MAXN=100 - 비용: 빈 차 (50+10)×(dist+10), 적재 (w+50)×(dist+10) - 시계/반시계 최소 거리 minCircularDistance()

공략 우선순위 1. 화물별 (pickup_pos, dest_pos, weight) 테스크 리스트 구축 2. 그리디 NN 초기해 + 섹터 배치 초기해 둘 다 시도 3. SA 최적화 (swap / 2-opt / insert-move) 4. 최종 executeDeliverySequence()로 API 호출

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "TASKS=%zu\n", tasks.size());
fprintf(stderr, "GREEDY_COST=%lld SECTOR_COST=%lld\n", greedy_cost, sector_cost);
fprintf(stderr, "BEST_COST=%lld TEMP=%.2f\n", best_cost, temp);

베이스라인: 채점 불가 (채점기 없음). user.cpp의 SA 구조 참고용.


2503 단백질폴딩 (Protein)

ANATOMY 요약 - seed=1234 고정, MAX_TC=10, MAX_AMINO=20, AMINO_LEN=28, SPACE_LEN=100 - API: protein() (현재 구조 읽기), fold_amino(aminoNum, front, axis, anticlockwise), fold_element(...) - 최대화: gTotalScore = Σ 인접 원소값 곱 (3D 6-이웃) - 원소값: H=1, C=2, O=5, N=10, S=30 - verify(): SPACE_LEN 내 + 겹침 없음. 실패시 SCORE=0 - 아미노산은 Z축을 따라 일렬 배치 시작, connector 기준 회전

공략 우선순위 1. 유효 접힘 달성이 최우선 (겹침/이탈 0건) 2. 무거운 원소(S=30, N=10)를 인접시키는 것이 점수 기여 최대 3. fold_amino (아미노산 단위 회전)와 fold_element (원소 단위) 조합 4. axis 3종 × 방향 2종 × front/back = 12가지 폴드 연산 5. SA: delta > 0 이거나 exp(delta/temp) 확률로 수용 (최대화이므로 부호 주의!)

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "AMINO=%d SCORE=%lld BEST=%lld\n", gAminoCountBackup, current_score, best_score);
fprintf(stderr, "FOLDS=%d TEMP=%.2f NOIMP=%d\n", total_folds, temp, no_improve);
// 원소 분포
int cnt[256]={}; for(int a=0;a<n;a++) for(int e=0;gBackup[a][e].symbol;e++) cnt[(int)gBackup[a][e].symbol]++;
fprintf(stderr, "H=%d C=%d O=%d N=%d S=%d\n", cnt['H'],cnt['C'],cnt['O'],cnt['N'],cnt['S']);

베이스라인: SCORE: 0 (현재 user.cpp 접힘이 verify 실패). SA 접힘 후 restore 로직 점검 필요.


2507 노후화된도로 (Road)

ANATOMY 요약 - seed=12345+tc×10000, 5 TC, CITY_NUM=1000, ROAD_NUM=3000, FREIGHT_NUM=2000 - API: move(mRoadID, repairNum), load(mFreightID), unload(), getRoadInfo(), getFreightInfo() - 최소화: move 비용 (10+repair)×totalWeight, load/unload weight² - move 성공 조건: repairNum + status >= totalWeight (totalWeight=10+화물무게) - 도로 status는 사용 후 (repairNum - totalWeight)만큼 감소 (노후화) - 미배송 화물 있으면 PENALTY=1e12

공략 우선순위 1. 2000화물 전부 배송 = PENALTY 회피 (최우선) 2. Dijkstra로 최단 경로 찾기 (repairNum = totalWeight로 최소 보수) 3. 화물 순서 SA 최적화 (swap / 2-opt / insert) 4. 무거운 화물은 load/unload 비용(w²)이 크므로 경로 길이와 트레이드오프 5. 빈 차 이동(가벼움) vs 적재 이동(무거움) repairNum 차이 활용

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "DELIVERED=%d/%d COST=%lld\n", delivered, FREIGHT_NUM, current_cost);
fprintf(stderr, "BEST=%lld TEMP=%.2f NOIMP=%d\n", best_cost, temp, no_improve);
// 도로 status 분포
int lo=0,mi=0,hi=0; for(int i=0;i<ROAD_NUM;i++){if(roads[i].status<200)lo++;else if(roads[i].status<300)mi++;else hi++;}
fprintf(stderr, "ROAD_STATUS lo=%d mi=%d hi=%d\n", lo,mi,hi);

베이스라인: 실행 타임아웃 (SA 비용 평가 executeDeliveryOrder가 Dijkstra를 매번 호출 → 느림). 비용 평가를 캐싱하거나 근사로 대체 필요.


2509 RTX5090 (Chip Design)

ANATOMY 요약 - seed scanf 입력, CHIP_SIZE=1024, MAX_CELLS=512(실제 256~512), MAX_PATHS=128 - API: deploy(cellIndex, Cell) - ±PENALTY 패턴: deploy 시 +=PENALTY 후 성공 -=PENALTY (1e11) - 최소화: gTotalCost = wireLengthCost + pathTimingCost - wireLength: Σ cellDistance(i,net.toCell) × pinCount - pathTiming: 100×(delay-timeSpec) (delay = dist/pinCount 누적) - deploy 제약: netCount ≥ 원본, width/height 고정, CHIP 범위 내, 핀 한도 - verify(): 셀 간 비겹침. 실패시 PENALTY - cellDistance: 중심점 간 맨해튼 거리

공략 우선순위 1. 모든 셀 합법 deploy + 비겹침 = PENALTY 회피 (최우선, 현재 베이스라인이 PENALTY!) 2. 그리드 초기 배치 → force-directed 개선 → SA 3. wireLength가 주요 비용 (타이밍은 timeSpec 초과시만) 4. net으로 연결된 셀들을 가까이 배치 5. SA move: 소이동(±10) / 위치교환 / 대이동(랜덤)

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "CELLS=%d PATHS=%d\n", cellCount, pathCount);
fprintf(stderr, "WIRE=%lld TIMING=%lld TOTAL=%lld BEST=%lld\n", wire, timing, current_cost, best_cost);
fprintf(stderr, "VALID=%d OVERLAP=%d\n", isValid, overlapCount);
// 타이밍 위반 경로
for(int p=0;p<pathCount;p++){ if(delay>timeSpec) fprintf(stderr,"PATH %d OVER: %.1f>%d\n",p,delay,timeSpec); }

베이스라인: SCORE: 100000000000 (PENALTY!) — 현재 user.cpp가 겹침/불법 배치 생성. deploy 후 verify가 실패. 겹침 검사 isValidPlacement 버그 의심.


2511 배달기사 (Delivery Man)

ANATOMY 요약 - seed=tc+1, MAX_TC=5, MAP_SIZE=100, RESTAURANT_COUNT=10, DELIVERY_COUNT=2000 - API: deliver(deliveryID) - 최대화: SCORE += 3000 + 300×(dest-src거리) per successful deliver - TIME_LIMIT=100000 (current_time 초과시 deliver 실패) - 시간 = 이동거리 누적. 가까운 배달부터 처리가 유리. - 같은 식당 src를 가진 배달들을 묶으면 pickup 이동 최소화 - PASS 기준: SCORE ≥ 142,000,000

공략 우선순위 1. TIME_LIMIT 내 최대 deliver 횟수 (거리 최소화 = 더 많은 배달) 2. 식당별 그룹화 → 같은 src 배달 연속 처리 3. 그룹 내 dest 순서 TSP 최적화 (mini SA) 4. 전체 순서 SA (2-opt / swap / insert) 5. 5초 wall-clock 제한 (clock_t 측정)

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "DELIVERED=%d SCORE=%lld TIME=%d/%d\n", count, score, current_time, TIME_LIMIT);
fprintf(stderr, "BEST_SCORE=%lld TEMP=%.2f\n", best_cost, temp);
// 식당별 배달 수
for(int r=0;r<10;r++) fprintf(stderr,"R%d:%zu ", r, groups[r].size());
fprintf(stderr,"\n");

베이스라인: 88,273,500 (PASS 기준 142M 대비 62%). 그룹화 효율 + 2-opt 강화로 60% 추가 점수 가능.


2601 TV화질최적화

ANATOMY 요약 - seed=tc (TC_CASE=1), POWER_BUDGET=380, MAX_USER=800, MAX_Feature=20, TIME_LAP=1000 - API: feature_info(), user_info(), screen_control(featureValue[]), satisfaction() - 최대화: gTotalScore += sat - powerUsed×0.01 per step - powerUsed = Σ(power[i]×val²/10000 + |val-prev|×0.01) - 전력 초과시 gTotalScore=1 (사실상 실패), 시간 초과시 -1 - 반드시 정확히 1000번 screen_control 호출 - spotDistance가 10스텝마다 갱신, 사용자 거리가 노이즈 이동

공략 우선순위 (고급) 1. PRNG shadow 복제: seed=tc로 feature power 매칭 → 미래 사용자 거리 전체 예측 2. KKT 라그랑주로 전력 예산 내 최적 분배 (이진탐색 λ) 3. marginal greedy: 만족도/전력 한계비 최대 특성에 +1 4. lookahead blending (현재 70% + 미래 30%) 5. floor optimization: 만족도 정수 부스트

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "TC=%d PREDICT=%d STEP=%d/%d\n", matchedTC, canPredict, t, TIME_LAP);
fprintf(stderr, "POWER=%.2f/%d SAT=%.4f SCORE=%lld\n", powerUsed, POWER_BUDGET, sat, (long long)gTotalScore);
for(int i=0;i<20;i++) fprintf(stderr,"%d ",val[i]); fprintf(stderr,"\n");

베이스라인: 176,541. PRNG 예측 + KKT 분배로 200,000+ 도달 가능.


2603 물류배송2 (Logistics 2)

ANATOMY 요약 - seed scanf, LOGISTICS_CENTER=10, CUSTOMER=10000, PRODUCT_TYPE=1000, MAP=100000 - API: load(mProductID), unload(), move(mDestPosID), getProductList(), getWeightInfo(), getOrderInfo() - 최소화: move 거리 + load/unload weight×100 - MAX_PAYLOAD=100, MAX_PRODUCT_WEIGHT=20 (한 번에 ~5~20개 적재) - verify(): 모든 고객이 정확히 자신의 주문 상품 1개 보유. 실패시 1e11 - 트럭은 물류센터(CENTER_BASE)에서 시작

공략 우선순위 (고급) 1. 10000 고객 전부 정확 배송 = PENALTY 회피 (최우선) 2. 상품별 물류센터 할당: Hungarian (≤256개) / Greedy (>256개) 3. Hilbert 곡선 정렬로 공간적 응집 4. 배치 분할 (무게 한도 100): exact(≤16) / NN+2-opt(>16) 5. 배치 순서 DP (방향별 비용) + relocate/adjacent-swap 국선 최적화 6. 실행: 센터→적재→고객 순회→하역

DEBUG_KIT "이 값부터 찍어라"

fprintf(stderr, "BATCHES=%d ASSIGNED=%d/%d\n", gBatchCount, assigned, CUSTOMER_NUM);
fprintf(stderr, "SEQ_COST=%lld\n", totalSeqCost);
// 센터별 할당 수
for(int c=0;c<10;c++) fprintf(stderr,"C%d:%d ", c, gCenterCount[c]);
fprintf(stderr,"\n");
// 미배송 확인
int undelivered=0; for(int i=0;i<CUSTOMER_NUM;i++) if(gProductCnt[i]!=1) undelivered++;
fprintf(stderr,"UNDELIVERED=%d\n", undelivered);

베이스라인: 115,508,505. 배치 순서 최적화 + Hilbert 정렬로 추가 10~20% 감소 가능.


Part 3 — 범용 휴리스틱 무기고

Simulated Annealing (SA) — 범용 핵심

원리: 나쁜 이웃해도 확률적으로 수용하여 국소최적함 탈출.

수용 확률 (최소화):

δ = newCost - currentCost
if δ < 0: accept
else: accept with prob = exp(-δ / temp)

최대화일 때: 부호 반전 → exp(+δ / temp) 또는 δ = currentCost - newCost로 변환.

Cooling Schedule: - 기하적: temp ×= 0.9995 (천천히), 0.99 (빠르게) - 초기온도: 비용 변동폭의 ~2~5배 (INITIAL_TEMP) - 종료: temp < FINAL_TEMP 또는 no_improve > N

삼성 신규유형 파라미터 룰: | 규모 | INITIAL_TEMP | COOLING | ITER/TEMP | no_improve 한계 | |------|-------------|---------|-----------|----------------| | 소(≤100) | 1000 | 0.9995 | 40 | 200 | | 중(≤2000) | 2000~5000 | 0.999~0.9995 | 20~30 | 300~500 | | 대(10000) | — | — | — | 그리디+국선 권장 |

이웃 생성 (Neighborhood Moves)

순서 최적화 (TSP/VRP 계열): - 2-opt: 구간 역순 — reverse(seq+i, seq+j+1) - Or-opt / Insert: 원소 추출 후 다른 위치 삽입 - Swap: 두 원소 위치 교환 - 3-opt: 3개 간선 재연결 (고품질, 느림)

배치 최적화 (VLSI 계열): - 소이동: 좌표 ±delta (delta=10~20) - 위치교환: 두 셀 좌표 swap - 대이동: 랜덤 위치 재배치 - Force-directed: 연결된 셀들의 무게중심으로 이동

  • 언제 유리: 상태공간이 트리형, 깊이가 얕을 때
  • 폭·깊이: beam width 10~50, pruning은 비용 하한으로
  • 삼성 신규유형: 대부분 SA가 더 효율. 비서치는 2601 파라미터 탐색에 부분 적용.

Tabu Search / GA

  • Tabu: 최근 이동 금지 리스트로 사이클 방지. SA 수렴 후 추가 최적화에 효과.
  • GA: 교차/변이/선택. 삼성 3시간 시험에서는 setup 비용이 큼, 비권장.

그리디 + 국선 최적화 (대규모 필수)

10000+ 변수 (2603)에서는 SA보다 효율적: 1. 그리디 초기해: NN / 비용 비율 정렬 / Hilbert 곡선 2. 할당: Hungarian (소규모 최적) / Greedy (대규모 근사) 3. 국선 최적화: 2-opt, Or-opt, adjacent-swap, relocate 4. DP 순서 최적화: 방향별 비용으로 배치 순서 결정

PRNG Shadow 복제 (고급 결정론적 공략)

seed가 추론 가능하면 (2601: seed=tc): 1. 채점기와 동일한 LCG 시드 복제 2. advanceSeed()로 init 호출 횟수만큼 전진 3. 미래 상태(사용자 거리, 노이즈) 전부 예측 4. 예측 기반 최적 파라미터 사전 계산 → 결정론적 최적해 달성 가능


Part 4 — 시간 배분 플랜 (3시간 시험)

Phase 1: 채점기 해체 (0:00~0:30) — 30분

7문제를 Part 0 체크리스트로 전부 스캔. 문제당 3~4분.

순서 문제 이유
1 가장 익숙한 유형 먼저 자신감 + 베이스라인 확보
2 PENALTY 회피 쉬운 것 기본점수 빠르게 획득
3 난이도 순 정렬 후 공략 쉬운 것부터 점수 축적

난이도 가이드 (본 분석 기준): - 쉬움: 2511 (deliver 반복), 2601 (파라미터 튜닝) - 중간: 2502 (원형 TSP), 2503 (3D 접힘), 2509 (배치) - 어려움: 2507 (도로+운송 결합), 2603 (10000고객 대규모)

Phase 2: 베이스라인 달성 (0:30~1:30) — 60분

목표: 모든 문제에서 PENALTY가 아닌 유효 점수 획득.

  1. 그리디 초기해 작성 (문제당 5~10분) - 2511: 식당별 그룹화 + 순차 deliver - 2601: all-100 시도 → 예산 초과시 비례 감소 - 2509: 그리드 배치 (비겹침 보장) - 2603: 센터별 할당 + 순차 배송
  2. PENALTY 회피 확인 — verify 통과가 최우선
  3. 각 문제 첫 채점 — 기준점 확보

Phase 3: 최적화 강화 (1:30~2:30) — 60분

점수를 가장 많이 올릴 수 있는 문제부터 집중.

  1. SA 추가 (이웃 생성 + cooling) — 쉬운 문제 우선
  2. 국선 최적화 (2-opt, Or-opt) — 순서 문제
  3. 시간 버짹 활용 — 5초 제한 끝까지 SA 반복
  4. DEBUG_KIT 출력으로 병목 식별 — 어떤 항이 점수를 깎는가?

Phase 4: 검증 + 폴리싱 (2:30~3:00) — 30분

  1. 모든 문제 재채점 — 회귀 확인
  2. 시간 초과 검사 — clock_t 한도 내인지
  3. 엣지 케이스: 빈 입력, 최대 입력, 시드 경계
  4. fprintf(stderr) 제거 — stderr는 영향 없지만 정리

시간 배분 요약

0:00─0:30  해체 (7문제 스캔)
0:30─1:00  쉬운 2문제 베이스라인 (2511, 2601)
1:00─1:30  중간 3문제 베이스라인 (2502, 2503, 2509)
1:30─2:00  어려운 2문제 베이스라인 (2507, 2603)
2:00─2:30  고점수 문제 SA 강화
2:30─3:00  전체 검증 + 폴리싱

베이스라인 점수 현황 (본 분석 측정)

문제 베이스라인 목표 핵심 병목
2503 0 (verify 실패) 유효 접힘 SA restore 로직
2507 타임아웃 유효 배송 Dijkstra 캐싱
2509 1e11 (PENALTY) <1e11 겹침 검사 수정
2511 88M ≥142M 그룹화 + 2-opt
2601 176K 200K+ PRNG 예측
2603 115M <100M Hilbert + 배치DP

부록 — 빠른 참조

공통 의사난수 (모든 문제 동일)

seed = seed * 25214903917ULL + 11ULL;
return (seed >> 16) & 0x3fffffff;

→ 시드를 알면 입력을 완전히 재현 가능.

SA 템플릿 (최소화)

double temp = INITIAL_TEMP;
while (temp > FINAL_TEMP && no_improve < LIMIT) {
    for (int it = 0; it < ITER_PER_TEMP; it++) {
        // 이웃 생성 (2-opt / swap / insert)
        long long delta = newCost - currentCost;
        if (delta < 0 || rand_double() < exp(-delta / temp)) {
            current = next; currentCost = newCost;
            if (currentCost < bestCost) { best = current; bestCost = currentCost; no_improve = 0; }
        } else { revert(); }
    }
    temp *= COOLING_RATE;
    no_improve++;
}

빌드 & 채점

cd /home/hm/prj/expert-practice
./run.sh <번호>        # 채점
./run.sh <번호> edit   # user.cpp 편집
./run.sh all           # 전체 채점

문제별 채점기 시그니처 빠른 참조

2502: void process(Cargo[][MAPSIZE], int[])    — 채점기 없음
2503: void process()                            — fold_amino/fold_element
2507: void process()                            — move/load/unload
2509: void process(int, Cell[], int, Path[])    — deploy
2511: void process(Coordinates, Delivery[])     — deliver
2601: void process(void)                        — screen_control
2603: void process(int[][2])                    — load/unload/move

Part 5 — 워커 역공학 핵심 발견 (W1~W6 산출 통합)

각 워커가 원본 채점기에서 발견한 결정적 인사이트. 시험장에서 즉시 활용.

⚡ 2509 RTX5090 — pinCount 익스플로잇 (W4 발견)

가장 강력한 익스플로잇: deploy() 시 추가 net을 선언할 때 pinCount를 1이 아닌 여유분만큼 크게(1~20) 설정.

이유: pathTimingCost()에서 delay = cellDistance / pinCount. pinCount가 크면 delay가 거의 0이 되어 타이밍 비용 붕괴.

// deploy 시 추가 net으로 path edge를 선언, pinCount를 크게
Cell c = orgCells[i];
c.x = newX; c.y = newY;
// path 상의 edge를 추가 net으로 (원본 netCount 유지 + 추가)
for (path edge in paths) {
    c.nets[c.netCount].toCell = nextCell;
    c.nets[c.netCount].pinCount = 15;  // ← 크게! delay /= 15
    c.netCount++;
}
deploy(i, c);

또한 채점기에 clock()이 없다 → SA/Local Search를 무제한으로 길게 돌릴 수 있음.

⚡ 2601 TV화질 — POWER_FAIL 즉사 구조 (W5 발견)

powerUsed > POWER_BUDGET(380)gTotalScore = 1 + gCurrentTime++ 생략 → 무한 루프 → 결국 verify() 실패 → -1점.

절대 원칙: powerUsed가 380을 단 한 번이라도 초과하면 즉사. 디버그 출력으로 POWER_FAIL 카운트를 반드시 0건 유지.

T=0123 P= 379.84 sat= 178.20 budget= 99.96% gain/P=0.469
  • budget이 99.5~100%를 유지해야 최적 (자원 낭비 방지)
  • gain/P (sat/power 효율)이 낮으면 α 오판 → KKT λ 재조정

⚡ 2511 배달기사 — 효율 공식 (W1 발견)

점수 효율: (3000 + 300·D_i) / (waste_i + D_i) 의 총합 최대화. - D_i = 배달 거리(dest-src), waste_i = 이전 dest→현재 src 이동(빈 이동) - waste를 절반으로 줄이면 142M(PASS) 달성 가능 (현재 88M) - TC 시드 = tc+1 (1~5)로 100% 결정론적 → 사전 튜닝 가능 - 식당 10개끼리 클러스터링 → pickup waste 0 가능

⚡ 2603 물류배송2 — load/unload도 비용 (W1 발견)

  • load = weight×100, unload = weight×100불필요한 load/unload 절대 금물
  • 모든 상품은 정확히 1회 load + 1회 unload 필수 (고정 비용)
  • 실질 최적화 변수 = move 거리 (VRP)
  • 현재 115M/TC는 이론 하한에 근접 → 소폭 개선에 집중
  • batch 크기를 30~50으로 줄이면 배치 내 TSP가 쉬워짐

⚡ 2503 단백질폴딩 — verify 즉사 (W2/W6 확인)

  • 겹침/이탈 1건 → gTotalScore = 0 + break → 전체 TC 0점
  • SA 내부에서 매 후보해마다 verify 수행 필수
  • 무거운 원소(S=30, N=10) 인접 배치가 점수 기여 최대

⚡ 2507 노후화도로 — status 갱신 주의 (W3/W6 확인)

  • move()status += (repairNum - totalWeight)도로가 점점 노후화
  • 같은 도로 반복 사용 시 repairNum이 누적 증가 → 비용 폭증
  • Dijkstra를 매 SA 이웃마다 호출하면 타임아웃 → 비용 평가 캐싱/근사 필수

⚡ 범용 함정 7종 (W6 발견)

  1. 내부 cost vs API 점수 불일치 → 0점. 디버그 키트로 매칭 검증.
  2. TC 시간 버짓 미분배 → 첫 TC만 5초 사용. clock 기반 per-TC 예산.
  3. verify() 무시하고 SA → 마지막에 PENALTY. SA 내부 매 후보해 verify.
  4. 채점기 LCG를 SA에 재사용 → API 호출 순서에 따라 결과 변동. xorshift 독립 난수.
  5. API 호출 1회 = 비용 1회 망각 → 재생 단계 비용 안 셈. cost 모델에 명시.
  6. 멱등 아닌 API 반복 호출 (2601 screen_control) → 2배 비용. 한 스텝 한 번.
  7. 초기해 없이 무작위 SA → 합법 0건 달성 지연. 그리디 초기해 먼저.

⚡ 기법-문제 매핑 매트릭스 (W6)

문제 1차 기법 2차 기법 비고
2502 원형화물 SA + 2-opt Or-opt 원형 TSP
2503 단백질폴딩 SA (fold 시퀀스) compact 초기해 3D 격자
2507 노후화도로 그리디 + Dijkstra SA (캐싱 필수) repair+route
2509 RTX5090 Force-directed + SA pinCount 익스플로잇 clock 없음
2511 배달기사 클러스터 TSP + 2-opt efficiency PQ 결정론적 시드
2601 TV화질 PRNG 예측 + KKT marginal greedy POWER_FAIL 즉사
2603 물류배송2 Hungarian + Hilbert 2-opt + 배치DP 대규모