Closed Jewan1120 closed 1 week ago
0 ≤ alp, cop, alp_req, cop_req ≤ 150
1 ≤ problems의 길이 ≤ 100
DP 로 풀이 시 O(150*150*100)
> 10^6
주어진 모든 문제들을 풀 수 있는 알고력과 코딩력을 얻는 최단시간 == 문제 중 최대 알고력과 코딩력 만큼 있으면 모든 문제를 다 풀 수 있음
dp[알고력][코딩력] = 최소시간
알고력 또는 코딩력을 높이는 3가지 방법
공부하기 (2)
⭐문제풀기 (1)
dp[solvedAlp][maxCop] = Math.min(dp[solvedAlp][maxCop], dp[i][j]+problem.cost);
dp[maxAlp][solvedCop] = Math.min(dp[maxAlp][solvedCop], dp[i][j]+problem.cost);
dp[solvedAlp][solvedCop] = Math.min(dp[solvedAlp][solvedCop], dp[i][j]+problem.cost);
dp[maxAlp][maxCop] = Math.min(dp[maxAlp][maxCop], dp[i][j]+problem.cost);
문제 풀기의 모든 경우에 대해 기존 최소시간 유지 vs 현재 푼 문제의 최소 시간
을 비교해서 구한다
특이사항
에어컨 하위버전으로 어려웠어요..
dp[알고력][코딩력] = 최소시간
까진 접근했는데, 이후에 문제를 풀었을 때 알고력,코딩력 에 따라 최소시간이 달라지는데 어떻게 점화실을 세워야할지 모르겠어서 해설 참고했어요😫 결국엔최대 알고력, 코딩력
에 대한 최소시간을 구하는 문제이기 때문에 이걸 기준으로 비교해서 최소시간을 유지할지 갱신할지 를 생각해야 했습니다
max_alp * max_cop * problems
의 길이 → 150 150 100 =2,250,000 2차원 DP이용
처음에 dp[i][j] = k: i번째로 j번째 문제를 풀었을때 최소 시간의 형태로 잘못 점화식을 생각했었습니다..
가능한 경우의 수 1) 한시간 공부해서 1알고력 or 1코딩력 기르기 2) 특정 문제를 풀고, 해당 문제를 통해 향상할 수 있는 만큼 알고력+코딩력 기르기
주의 사항
int nxA = Math.min(i + pb[2], mxA);
mxA = Math.max(mxA, p[0]);
dfs를 이용한 완전탐색을 생각했다가 dp생각했다가 한,, DP공부 연습을 더더 해야겠네요 DP 공부하기에 좋은 문제인 것 같습니당
O(목표 알고력 * 코딩 알고력 * 문제 길이)
< 1억모든 문제를 풀 수 있는 최소 시간 반환(최적의 해)
완전 탐색으로 풀기엔 능력치 증가 조건 경로 다양, 중복된 상태 계산 많음 -> DP
int[][] dp = new int[tA + 1][tC + 1]; // dp[알고력][코딩력]
능력치 증가 조건
알고력+1
if (i + 1 <= tA) { // 알고력 +1 경우
dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j] + 1);
}
코딩력 +1
if (j + 1 <= tC) { // 코딩력 +1 경우
dp[i][j + 1] = Math.min(dp[i][j + 1], dp[i][j] + 1);
}
문제를 풀어서 능력치 올리기
// 문제를 풀어서 능력치 올리기
for (int[] problem : problems) {
int alp_req = problem[0]; // 변수 할당
int cop_req = problem[1];
int alp_rw = problem[2];
int cop_rw = problem[3];
int cost = problem[4];
if (i >= alp_req && j >= cop_req) {
int nA = Math.min(tA, i + alp_rw);
int nC = Math.min(tC, j + cop_rw);
dp[nA][nC] = Math.min(dp[nA][nC], dp[i][j] + cost);
}
}
dp[tA][tC] 반환 후 종료
처음에 완전 탐색으로 접근 했었다가 고려할 경우가 많아서 다른 방법이 있는지 찾아보니까 DP로 풀 수 있었네요! DP는 처음에 떠올리기가 어려운 것 같습니다
DP
를 이용한 탐색DP
// 알고력 증가
if (i < maxAlp)
dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j] + 1);
// 알고력 증가
if (j < maxCop)
dp[i][j + 1] = Math.min(dp[i][j + 1], dp[i][j] + 1);
// 문제 풀기
for (int[] problem : problems)
// 풀 수 있는 문제라면
if (i >= problem[0] && j >= problem[1]) {
// 능력치 증가
int nextAlp = Math.min(maxAlp, i + problem[2]);
int nextCop = Math.min(maxCop, j + problem[3]);
dp[nextAlp][nextCop] = Math.min(dp[nextAlp][nextCop], dp[i][j] + problem[4]);
}
이번 문제도 현재 값을 결정하는 게 아니라 다음 값을 결정해야하는 문제였습니다
dp[r][c]
: 알고력이 r이고, 코딩력이 c가 되기 위해 걸리는 최소 시간을 구함
150(alp_req) * 150(cop_req) * 6(problems)
int maxAlpReq, maxCopReq
: problem
중에서 필요한 알고력, 코딩력 중 가장 큰 값int[][] dp = new int[maxAlpReq + 1][maxCopReq + 1]
problems
있는 최대 alp_req
, cop_req
가 되면 모든 문제를 풀 수 있기 때문에 크기를 [maxAlpReq + 1][maxCopReq + 1]
로 설정함alp
와 cop
가 dp
범위를 벗어날 수 있기 때문에 값을 재조정해 줌dp
배열 값 채우기
r
과 c
에 대해 순차적으로 탐색하면서 값을 채움
// 알고리즘을 공부해서 알고력을 1 높이는 경우
if(r + 1 < dp.length) dp[r + 1][c] = Math.min(dp[r + 1][c], dp[r][c] + 1);
// 코딩 공부를 해서 코딩력을 1 높이는 경우 if(c + 1 < dp[0].length) dp[r][c + 1] = Math.min(dp[r][c + 1], dp[r][c] + 1);
// 풀 수 있는 문제를 하나 풀어 알고력과 코딩력을 높이는 경우 for(int k = 0; k < problems.length; k++) { int alpReq = problems[k][0]; // 문제를 풀기 위해 필요한 알고력 int copReq = problems[k][1]; // 문제를 풀기 위해 필요한 코딩력 int alpRwd = problems[k][2]; // 문제를 풀었을 때 증가하는 알고력 int copRwd = problems[k][3]; // 문제를 풀었을 때 증가하는 코딩력 int cost = problems[k][4]; //문제를 푸는데 드는 시간
if(r < alpReq || c < copReq) continue; // 현재 알고력이나 코딩력이 부족한 경우
// 범위를 벗어나는 경우 continue를 하면 오답! // 범위를 벗어나면 무조건 문제를 풀 수 있는 것이기 때문에 maxAlpReq, maxCopReq가 된 것이라고 생각해야 됨 // if(dp.length < r + alpRwd || maxCopReq < c + copRwd) continue; // 범위를 벗어가는 경우 // dp[r + alpRwd][c + copRwd] = Math.min(dp[r + alpRwd][c + copRwd], dp[r][c] + cost); int nextAlp = Math.min(maxAlpReq, r + alpRwd); int nextCop = Math.min(maxCopReq, c + copRwd);
dp[nextAlp][nextCop] = Math.min(dp[nextAlp][nextCop], dp[r][c] + cost); }
다익스트라로도 풀린다고 하는데,,,, 잘 모르겠네요!! 리뷰 끝나고 한 번 다익스트라로 다시 풀어보겠습니당
💡 풀이 아이디어