비교하고자 하는 배열 a의 크기는 항상 배열 b보다 작다.
배열 a로부터 차이를 최소로 하는 배열 {bi,bi+1,…,bj} (0≤i≤j≤n) 을 찾는다.
스위핑으로 되려나싶어서 코드를 다음과 같이 작성해봤다.
int ptr = -1, sum = 0;
for(int i=0; i<n; ++i){
int val = INF;
for(int j=ptr+1; j<=m-(n-i); ++j){
if(val > abs(a[i]-b[j])){
val = abs(a[i]-b[j]);
ptr = j;
}
}
sum += val;
}
return sum;
그러나 아래와 같이 이전에 작은 손해가 있지만 결과적으로 최소가 나오는 케이스에서 오답을 출력했다.
3 6 10 20 30 9 10 11 100 101 102 A = [10, 20, 30] B = [ 9, 10, 11]이여야하는데, 원소가 하나씩 밀려서 B = [10, 11, 100] 가 되버린 것이다.
이거 DP구나 깨닫고 O(|A||B|) 를 다 해보자 싶었다.
int solve(int apos, int bpos){
if(apos == n) return 0;
int& r = dp[apos][bpos];
if(r != -1) return r;
r = INF;
for(int i=bpos; i<=m-(n-apos); ++i){
r = min(r, abs(a[apos]-b[i]) + solve(apos+1, i+1));
}
return r;
}
범위를 |B| 배열 길이만큼이 아닌, 남은 |A|의 길이만큼으로 했지만 역시 느렸다. 속도가 빠른 소스들을 보니 이렇게 해결한 것 같다.
min(b[bpos]를 선택하는 경우, 선택하지 않고 b[bpos+1]과 a[pos]를 매칭하는 경우 )
재귀가 2번씩만 호출되니까 엄청 빨라졌다.
#include <bits/stdc++.h>
using namespace std;
#define INF 987654321
typedef long long lld;
int n, m;
vector<int> a, b;
int dp[1001][1001];
int solve(int apos, int bpos){
if(apos == n) return 0;
if(bpos == m) return INF;
int& r = dp[apos][bpos];
if(r != -1) return r;
// bpos를 선택한다
r = abs(a[apos]-b[bpos]) + solve(apos+1, bpos+1);
// 안 한다 (다음으로 미룬다)
r = min(r, solve(apos, bpos+1));
return r;
}
int main(){
memset(dp, -1, sizeof(dp));
scanf("%d %d", &n, &m);
a.resize(n);
b.resize(m);
for(int i=0; i<n; ++i) scanf("%d", &a[i]);
for(int i=0; i<m; ++i) scanf("%d", &b[i]);
sort(a.begin(), a.end());
sort(b.begin(), b.end());
if(n > m){
// a(n)이 항상 작은 배열
swap(a, b);
swap(n, m);
}
printf("%d", solve(0, 0));
return 0;
}
댓글 없음:
댓글 쓰기