코테연습일지/DP

DP(1) - 연습문제 풀기

jemin0619 2024. 2. 14. 02:42

https://www.youtube.com/watch?v=5leTtB3PQu0&t=15s

 

어쩌다 보니 이제 DP를 배울 때가 왔다. 지금까지 해왔던 것들 중 DP가 가장 어렵게 느껴졌다... 이것도 많이 해보면 BFS처럼 늘 것이라 믿는다.


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

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

#include <bits/stdc++.h>
using namespace std;
int d[1000003];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n; cin>>n;
	d[1]=0;
	for(int i=2;i<=n;i++){
		//기본 상태 
		d[i]=d[i-1]+1;
		
		//나누어 떨어질 경우 
		if(i%2==0) d[i]=min(d[i],d[i/2]+1);
		if(i%3==0) d[i]=min(d[i],d[i/3]+1);
	}
	cout<<d[n];
	return 0;
}

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

 

9095번: 1, 2, 3 더하기

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

www.acmicpc.net

#include <bits/stdc++.h>
using namespace std;
int d[13];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	d[1]=1; d[2]=2; d[3]=4;
	for(int i=4;i<11;i++){
		d[i]=d[i-1]+d[i-2]+d[i-3];
	}
	int k; cin>>k;
	while(k--){
		int n; cin>>n;
		cout<<d[n]<<'\n';
	}
	return 0;
}

이 문제의 점화식은 간단해서 쉽게 풀 수 있다.


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

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net

이 문제가 너무 어렵게 느껴졌는데 한 번 그림으로 그려보니 이해할 수 있었다. 

#include <bits/stdc++.h>
using namespace std;
int d[303][3]; //계단 연속으로밟은계단(3개면 안됨) 
int s[303]; 
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n; cin>>n;
	for(int i=1;i<=n;i++) cin>>s[i];
	d[1][1]=s[1]; d[1][2]=0; //불가능함 
	d[2][1]=s[2]; d[2][2]=s[1]+s[2];
	for(int i=3;i<=n;i++){
		d[i][1]=max(d[i-2][1],d[i-2][2])+s[i];
		d[i][2]=d[i-1][1]+s[i];
	}
	cout<<max(d[n][1],d[n][2]);
	return 0;
}

 

이 부분에서 한 번 벽을 느꼈었다.

 

영상에서는 1차원 배열로 이 문제를 푸는 법도 알려준다. 밟을 계단을 고르지 않고, 밟지 않을 계단을 고르는 것이다. 

D[i] : i번째 계단까지 올라섰을 때 밟지 않을 계단의 합의 최솟값. (단 i번째 계단은 반드시 밟지 않을 계단임)

이때 i번째 계단을 밟지 않는다는 말은 i-1 번째 계단은 무조건 밟는다는 의미이다. 

i-1번째 계단을 밟으려면 i-2, 혹은 i-3번째 계단을 밟아야 한다(=i-3번째 계단을 밟지 않거나, i-2번째 계단을 밟지 않는다.) 


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

 

1149번: RGB거리

첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나

www.acmicpc.net

이 문제는 혼자 풀어보았다. 풀만한 문제였다.

#include <bits/stdc++.h>
using namespace std;
int d[1003][3]; //집 색깔 
pair<pair<int,int>,int> RGB[1003];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int N; cin>>N;
	for(int i=1;i<=N;i++){
		int R,G,B; cin>>R>>G>>B;
		RGB[i]={{R,G},B};
	}
	d[1][0]=RGB[1].first.first;
	d[1][1]=RGB[1].first.second;
	d[1][2]=RGB[1].second;
	for(int i=2;i<=N;i++){
		d[i][0]=min(d[i-1][1],d[i-1][2])+RGB[i].first.first;
		d[i][1]=min(d[i-1][0],d[i-1][2])+RGB[i].first.second;
		d[i][2]=min(d[i-1][0],d[i-1][1])+RGB[i].second;
	}
	cout<<min(min(d[N][0],d[N][1]),d[N][2]);
	return 0;
}

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

 

11726번: 2×n 타일링

2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.

www.acmicpc.net

강의를 보면서 진짜 감탄했다. 이런 생각이 물흐르듯이 나와야 카이스트 가고... 과학고 가고... 

결국 1,2,3 더하기와 다를 것 없는 문제였다.

#include <bits/stdc++.h>
using namespace std;
int d[1003];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n; cin>>n;
	d[1]=1; d[2]=2;
	for(int i=3;i<=n;i++){
		d[i]=(d[i-1]+d[i-2])%10007;
	}
	cout<<d[n];
	return 0;
}

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

 

11659번: 구간 합 구하기 4

첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다. 셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j

www.acmicpc.net

이 문제 어디선가 푸는 법을 배운 것 같다. 머리속에 풀이가 있어서 그냥 풀었다.

이 기법을 Prefix-sum 이라고 한다고 한다.

#include <bits/stdc++.h>
using namespace std;
int d[100003],input[100003];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n,m; cin>>n>>m;
	for(int i=1;i<=n;i++){cin>>input[i]; d[i]=d[i-1]+input[i];}
	while(m--){
		int a,b; cin>>a>>b;
		cout<<d[b]-d[a-1]<<'\n';
	}
	return 0;
}