https://www.acmicpc.net/problem/16957

 

16957번: 체스판 위의 공

크기가 R×C인 체스판이 있고, 체스판의 각 칸에는 정수가 하나씩 적혀있다. 체스판에 적혀있는 정수는 모두 서로 다르다. 체스판의 각 칸 위에 공을 하나씩 놓는다. 이제 공은 다음과 같은 규칙

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
union-find의 find와 비슷한 개념을 이용하면 시간초과에 걸리지 않고 해결할 수 있습니다.
pair<int,int> dest[501][501] 를 선언하여 dest[y][x] 에는 y,x에서 출발 했을 때 최종으로
도착하는 점 {dest_y,dest_x}를 저장해 주었습니다.
board[y][x]의 수가 높은 수부터 탐색을 해주어야 하며
탐색 도중 다음 가야하는 점의 dest 값이 이미 구해져 있다면 이 값을 그대로 이용하며 탐색을 종료하면
시간 초과를 피할 수 있습니다.

 

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
vector<pair<int,pair<int,int>>> v;
int R,C,board[501][501],cnt[501][501];
int dy[8] = {-1,1,0,0,1,-1,1,-1};
int dx[8] = {0,0,1,-1,1,-1,-1,1};
pair<int,int> dest[501][501];
pair<int,int> sol(int y,int x){
    int minV=4e5,my,mx;
    for(int i=0;i<8;i++){
        int ny=y+dy[i], nx=x+dx[i];
        if(ny<1||nx<1||ny>R||nx>C) continue;
        if(board[ny][nx] < minV){
            minV=board[ny][nx];
            my=ny;
            mx=nx;
        }
    }
    if(board[y][x] > minV){
        if(dest[my][mx].first!=0) return dest[y][x] = dest[my][mx];
        return dest[y][x] = sol(my,mx);
    }
    return dest[y][x]={y,x};
}
int main(){
    scanf("%d%d",&R,&C);
    for(int i=1;i<=R;i++)
        for(int j=1;j<=C;j++){
            scanf("%d",&board[i][j]);
            v.push_back({board[i][j],{i,j}});
        }
    sort(v.begin(),v.end());
    reverse(v.begin(),v.end());
    for(auto p : v){
        auto [y,x] = p.second;
        if(dest[y][x].first==0) sol(y,x);
    }
    for(int i=1;i<=R;i++){
        for(int j=1;j<=C;j++){
            auto [y,x] = dest[i][j];
            cnt[y][x]++;
        }
    }
    for(int i=1;i<=R;i++){
        for(int j=1;j<=C;j++){
            printf("%d ",cnt[i][j]);
        }
        puts("");
    }   
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold4/16957.cpp

https://www.acmicpc.net/problem/1943

 

1943번: 동전 분배

세 개의 입력이 주어진다. 각 입력의 첫째 줄에 동전의 종류 N(1 ≤ N ≤ 100)이 주어진다. 각 입력의 둘째 줄부터 N+1째 줄까지 각각의 동전의 금액과 개수가 빈 칸을 사이에 두고 주어진다. 단, 원

www.acmicpc.net

 

 

[난이도] Gold3
[유형] DP

[풀이]
N개의 물건이 1개 이상일 수 있는 배낭 문제입니다.
기본적인 배낭 문제의 풀이와 같으며,
다른 점은
{물건의 가치, 물건의 개수} pair로 N개를 저장한 뒤
i번 물건을 1,2,3,, (총 개수) 만큼 사용해볼 때의 모든 경우를 고려해주면 됩니다.
3중 for문이 나오기 때문에 시간초과에 걸릴 수 있어 답을 찾은 즉시 종료해주어야 합니다.

 

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int N,total,dp[50001];
pair<int,int> a[100];
int sol(){
    memset(dp,0,sizeof(dp));
    total=0;
    for(int i=0;i<N;i++) {
        scanf("%d%d",&a[i].first,&a[i].second);
        total+=a[i].first * a[i].second;
    }
    if(total%2) return 0;
    dp[0]=1;
    for(int i=0;i<N;i++){
        for(int j=total/2;j>=0;j--){
            if(dp[j]) continue;
            int cur=0;
            for(int k=0;k<a[i].second;k++){
                cur+=a[i].first;
                if(j-cur>=0)  dp[j]=dp[j-cur];     
                if(dp[total/2]) return 1;
            }
        }
    }
    return dp[total/2];
}
int main(){
    int tc=3;
    while(tc--) {
        scanf("%d",&N);
        printf("%d\n",sol());
    }
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold3/1943.cpp

https://www.acmicpc.net/problem/1757

 

1757번: 달려달려

어제, 그리고 어제 어제 단체달리기를 두 번이나 하였다. 원장선생님의 이러한 하드 트레이닝으로 월드 학생들의 체력은 거의 박지성 수준이 되었다. 그래서 월드 학생들은 운동장을 도는데 정

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
2차원 DP 문제 입니다.

DP[n][m] : 현재 지침 지수가 m이고 n분일 N-1분까지 달릴 수 있는 최대 거리

점화식은 n번 분에서 뛰거나 뛰지 않는 경우로 나누어서 생각할 수 있습니다.

Top-Down DP를 사용하였기 때문에 sol(n,m) 은 DP[n][m]을 구하고 return 합니다.

case 1) 뛰는 경우
    뛰면 m 값이 1증가 해야 하는데 만약 m+1>M이 된다면 문제의 조건에 의해 더이상 뛸 수 없으므로
    뛰어서는 안됩니다.
    m+1<=M 이라면 sol(n,m) = sol(n+1,m+1)+a[n] 이 됩니다.

case 2) 뛰지 않는 경우
    m>0 이라면 문제의 조건에 의해 m이 0이 될 때까지 쉬어야 하고
    적어도 마지막 분까지 쉬었을 때 m이 0이 되어야 하므로 n+m<=N 인 경우에만
    sol(n,m) = sol(n+m,0) 이 됩니다. (m은 다 쉬었기 때문에 0이 됨)

    만약 m==0 이라면 m값은 변화 없이 sol(n,m) = sol(n+1,0) 이 됩니다.

위의 값들 중 최댓값을 최종 sol(n,m)의 값으로 return 해야 합니다.

종료 조건 (n==N)
    n==N일 때 m은 0이어야 하므로 0이 아니라면 아주 큰 음수 inf를 return하여 최종 답에서 무시되도록 합니다.

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int N,M,dp[10001][502],a[10001],inf=-9e8;
int sol(int n,int m){
    if(n==N) {
        if(m==0) return 0;
        return inf;
    }
    int& ret = dp[n][m];
    if(ret!=-1) return ret;
    ret=inf;
    if(m>0) {
        if(n+m<=N) ret=max(ret,sol(n+m,0));
    }else {
        ret=max(ret,sol(n+1,0));
    }

    if(m+1<=M) ret=max(ret,sol(n+1,m+1)+a[n]);
    return ret;
}
int main(){
    scanf("%d%d",&N,&M);
    for(int i=0;i<N;i++) scanf("%d",&a[i]);
    memset(dp,-1,sizeof(dp));
    printf("%d",sol(0,0));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold4/1757.cpp

https://www.acmicpc.net/problem/1354

 

1354번: 무한 수열 2

첫째 줄에 5개의 정수 N, P, Q, X, Y가 주어진다.

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
map을 이용해 메모제이션을 해주면 long long 형의 큰 수도 저장할 수 있습니다.
i/P, i/Q와 같이 P,Q로 나눈 후의 수들만 필요하기 때문에 너무 많은 수가 저장되는 것을 저장할 필요가 없습니다.

 

#include <cstdio>
#include <map>
using namespace std;
using ll = long long;
ll N,P,Q,X,Y;
map<ll,ll> dp;
ll sol(ll n){
    if(n<=0) return 1;
    if(dp.find(n)!=dp.end()) return dp[n];
    return dp[n] = sol(n/P-X)+sol(n/Q-Y);
}
int main(){
    scanf("%lld%lld%lld%lld%lld",&N,&P,&Q,&X,&Y);
    printf("%lld",sol(N));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold4/1354.cpp

https://www.acmicpc.net/problem/1301

 

1301번: 비즈 공예

첫째 줄에 다솜이가 가지고 있는 구슬의 종류 N이 주어진다. N은 3보다 크거나 같고, 5보다 작거나 같다. 둘째 줄부터 N개의 줄에 각각의 구슬이 총 몇 개 있는 지주어진다. 첫째 줄에는 1번 구슬,

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
구슬의 종류가 최대 5개밖에 되지 않고, 각각 10개 이하이기 때문에 7차원 DP로 해결할 수 있습니다.
long long dp[7][7][11][11][11][11][11] 배열을 이용하였습니다.
dp[prev][cur][a][b][c][d][f]에서 cur은 이전에 선택한 구슬의 색, prev는 cur 이전에 선택한 구슬의 색이며
a,b,c,d,f는 각각 구슬의 남은 개수입니다.

prev와 cur이 아닌 구슬을 선택하는 경우의 수를 더해주면 됩니다.
모든 구슬을 사용했을 때가 가능한 경우의 수가 되므로 1을 return 해서 정답에 포함될 수 있게 해줍니다.

 

#include <cstdio>
#include <cstring>
using namespace std;
using ll = long long;
int N,a[6];
ll dp[7][7][11][11][11][11][11];
ll sol(int prev,int cur,int a,int b,int c,int d,int f){
    if(a==0&&b==0&&c==0&&d==0&&f==0) return 1;
    ll& ret=dp[prev][cur][a][b][c][d][f];
    if(ret!=-1) return ret;
    ret=0;
    if(a>0 && prev!=0 && cur!=0) ret+=sol(cur,0,a-1,b,c,d,f);
    if(b>0 && prev!=1 && cur!=1) ret+=sol(cur,1,a,b-1,c,d,f);
    if(c>0 && prev!=2 && cur!=2) ret+=sol(cur,2,a,b,c-1,d,f);
    if(d>0 && prev!=3 && cur!=3) ret+=sol(cur,3,a,b,c,d-1,f);
    if(f>0 && prev!=4 && cur!=4) ret+=sol(cur,4,a,b,c,d,f-1);
    return ret;
}
int main(){
    scanf("%d",&N);
    for(int i=0;i<N;i++) scanf("%d",&a[i]);
    memset(dp,-1,sizeof(dp));
    printf("%lld",sol(6,5,a[0],a[1],a[2],a[3],a[4]));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold4/1301.cpp

https://www.acmicpc.net/problem/1099

 

1099번: 알 수 없는 문장

첫째 줄에 문장이 주어진다. 문장의 길이는 최대 50이다. 둘째 줄에 단어의 개수 N이 주어지며, N은 50보다 작거나 같은 자연수이다. 셋째 줄부터 N개의 줄에 각 단어가 주어진다. 단어의 길이는 최

www.acmicpc.net

 

 

[난이도] Gold3
[유형] DP

[풀이]
단어의 개수와 길이가 50으로 작기 때문에 다이나믹 프로그래밍으로 해결이 가능합니다.

dp 배열은 다음과 같이 정의가 가능하며
dp[n] : 문장 string을 s라고 했을 때, s[n~s.size()-1] 인 substring을 해석하기 위해 필요한 최소 연산

점화식은 다음과 같습니다.
dp[n] = dp[i+1] + [s[n~i]인 substring을 해석하는데 필요한 cost] ( n<=i<s.size()-1)

모든 N개의 단어를 substring s[n~i]으로 변환하는데 드는 cost 중 최솟값을 위의 cost로 사용하면 됩니다.
아래 코드에서는 일단 비교하는 두 string을 정렬하여 비교함으로써 정확히 같은
문자들을 가지고 있는지 확인하였고, 원본 string을 다시 비교하면서 위치가 다른 문자들의 개수를 세서
전체 문자 길이에서 빼주었습니다.

 

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
string s,word[50];
int dp[51],N,inf=9e8;
int cost(string a,string b){
    if(a.size()!=b.size()) return inf;
    string ta = a;
    string tb = b;
    sort(ta.begin(),ta.end());
    sort(tb.begin(),tb.end());
    if(ta!=tb) return inf;

    int ret = a.size();
    for(int i=0;i<a.size();i++){
        if(a[i]==b[i]) ret--;
    }
    return ret;
}
int minCost(string k){
    int ret = inf;
    for(int i=0;i<N;i++) ret = min(ret,cost(k,word[i]));
    return ret;
}
int sol(int n){
    if(n==s.size()) return 0;
    int& ret = dp[n];
    if(ret!=-1) return ret;
    ret = inf;
    for(int i=n;i<s.size();i++){
        string k = s.substr(n,i-n+1);
        int c = minCost(k);
        if(c==inf) continue;
        ret = min(ret,c+sol(i+1));
    }
    return ret;
}
int main(){
    ios_base::sync_with_stdio(false); 
    cin.tie(0);
    cin >> s >> N;
    for(int i=0;i<N;i++) cin >> word[i];
    memset(dp,-1,sizeof(dp));
    printf("%d",sol(0)==inf ? -1 : sol(0));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold3/1099.cpp

https://softeer.ai/practice/result.do?eventIdx=1&psProblemId=405&submissionSn=SW_PRBL_SBMS_19088#hold

 

https://softeer.ai/practice/result.do?eventIdx=1&psProblemId=405&submissionSn=SW_PRBL_SBMS_19088#hold

 

softeer.ai

 

[난이도] level5
[유형] DP

[풀이]
K의 제한이 10^4로 증가하였기 때문에 복잡한 조립라인1 문제처럼 O(N*K^2) 풀이로는 시간내에 해결이 불가능 합니다.
대신 모든 j 작업장에서 다른 라인의 j+1 로 작업장으로 넘어가는데 걸리는 시간이 동일하기 때문에

Bottom-up 방식으로 dp[i][j] (1<=i<=K) 를 계산하기 전에
dp[i][j-1] (1<=i<=K) 중 최솟값 v를 미리 구해주면 아래와 같은 점화식을 세울 수 있습니다.

dp[i][j] = min(v+time[i][j]+mvtime[j-1],dp[i][j-1]+time[i][j]);

case 1) v+time[i][j]+mvtime[j-1] 은 j-1에서 j 작업으로 이동할 때 라인을 바꾼 경우입니다.
이 경우에는 이동간인 mvtime[j-1]을 더해준 것을 알 수 있습니다.

case 2) dp[i][j-1]+time[i][j] 은 라인을 바꾸지 않고 i라인에서 그대로 작업한 경우 입니다. mvtime[j-1]을 더해주지 않았습니다.

case 1에 라인을 바꾸지 않은 케이스 dp[i][j-1]+time[i][j]+mvtime[j-1] 가 포함되어 있습니다. 이때는
mvtime[j-1]을 더하지 않아야 하는데 더해서 문제가 발생할 것 같지만 이 값은 어차피
case 2인 dp[i][j-1]+time[i][j] 값보다 작아 무시되게 되어 문제가 되지 않습니다.

위와 같은 방법으로 O(N*K) 시간에 해결이 가능합니다.

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int K,N,time[10001][101],mvtime[101],dp[10001][101],inf=2e9;
int main(){
    scanf("%d%d",&K,&N);
    for(int j=1;j<=N;j++){
        for(int i=1;i<=K;i++) {
            scanf("%d",&time[i][j]);
            dp[i][j]=inf;
        }
        if(j==N) break;
        scanf("%d",&mvtime[j]);
    }
    for(int j=1;j<=N;j++){
        int v=inf;
        for(int i=1;i<=K;i++) v=min(v,dp[i][j-1]);
        for(int i=1;i<=K;i++) dp[i][j] = min(v+mvtime[j-1]+time[i][j],dp[i][j-1]+time[i][j]);
    }
    int ans=inf;
    for(int i=1;i<=K;i++) ans=min(dp[i][N],ans);
    printf("%d",ans);
}


https://github.com/has2/Problem-Solving/blob/master/softeer/level5/복잡한_조립라인2.cpp

https://softeer.ai/practice/info.do?eventIdx=1&psProblemId=404&sw_prbl_sbms_sn=18720

 

Softeer

제한시간 : C/C++(1초), Java/Python(2초) | 메모리 제한 : 256MB 동일한 자동차를 생산하는 K개의 조립라인 Li (1 ≤ i ≤ K)가 있다. 한 조립라인에는 각각 N개의 작업장이 있다. 각각의 작업장을 Li, j (1 ≤ i

softeer.ai

 

 

[난이도] level4
[유형] DP

[풀이]
다이나믹 프로그래밍을 이용해서 해결할 수 있습니다.
time[k][n] : k번 라인의 n번 작업장의 작업시간
mvtime[k][n][i] (i!=k) : k번 라인의 n번 작업장에서 i번 라인의 n+1번 라인으로 옮길때의 시간
dp[k][n] : k번 라인의 n번 작업부터 시작했을 때 N번 작업을 끝날때까지 걸리는 최소 시간

Top-Down DP를 이용하였으므로
sol(k,n) 은 dp[k][n]을 저장하며 리턴합니다.

점화식은 아래와 같습니다.
for(int i=1;i<=K;i++) dp[k][n]=min(dp[k][n],sol(i,n+1)+time[k][n]+mvtime[k][n][i]);
현재 작업 라인 k에서 1~K번 라인으로 넘어갈 수 있으며 그때의 이동시간인 mvtime을 더해주는 것을 알 수 있습니다.

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int K,N,time[101][101],mvtime[101][101][101],dp[101][101],inf=2e9;
int sol(int k,int n){
    if(n==N) return time[k][n];
    int& ret = dp[k][n];
    if(ret!=-1) return ret;
    ret=inf;
    for(int i=1;i<=K;i++) ret=min(ret,sol(i,n+1)+time[k][n]+mvtime[k][n][i]);
    return ret;
}
int main(){
    scanf("%d%d",&K,&N);
    for(int j=1;j<=N;j++){
        for(int i=1;i<=K;i++) scanf("%d",&time[i][j]);
        if(j==N) break;
        for(int i=1;i<=K;i++)
            for(int k=1;k<=K;k++)
                if(i!=k) scanf("%d",&mvtime[i][j][k]);
    }
    memset(dp,-1,sizeof(dp));
    printf("%d",sol(0,0));
}


https://github.com/has2/Problem-Solving/blob/master/softeer/level4/복잡한_조립라인1.cpp

+ Recent posts