728x90
개요
문제 링크
골드 4, Knapsack, DP
1부터 N까지의 수열의 부분수열의 합이 전체 합의 절반이 되도록 하는 경우의 수
접근
SSP의 Knapsack을 쓰는 문제, O(2^n)이나 O(2×2^(n/2))가 O(nS)보다 현저히 크기 때문에 Knapsack을 쓰는 게 이득이다. 시간을 비교해보자.
$O(2^n)$ $O(2\times 2^{n\over 2})$ $O(nS)$ 2^50 2^26 50×(50×51/2) 1e15 6.7e7 64000 Meet in the middle로도 풀 수는 있다. 652ms가 나온다. Knapsack의 연산 횟수가 대략 1000분에 1 정도 되니 1ms가 안돼서 Knapsack은 0ms가 나왔다. 둘 다 아래에 추가해두었다.
Knapsack, 배낭 문제로 이 문제를 풀어보자. 이 내용은 SSP에 있는 내용과 거의 똑같으니 더 자세한 접근을 보고 싶으면 참조하자.
i번째 원소 이전의 원소를 활용하면서 합을 j로 만드는 경우의 수를 dp[i][j]라고 하면, 다음의 관계가 성립한다.
dp[i][j]=dp[i-1][j]+dp[i-1][j-a[i]]
현재 원소를 선택하지 않는 것과 현재 원소를 선택하는 것을 더해준다. 이때 문제에서 a[i]=i이다.
이러면 어차피 2차원 dp에서 두 행만 사용하므로 두개의 행벡터를 서로 복사해가며 문제를 풀어주면 메모리를 아낄 수 있다.
Source code
// Knapsack 0ms
#include <bits/stdc++.h>
using namespace std;
using ld=long long;
using vec=vector<ld>;
ld n,S;
int main() {
cin>>n;
S=n*(n+1)/2;
vec b(S,0),c(S,0);
c[0]=1;
if (S%2==0)
for (int i=1;i<=n;i++) {
b=c;
for (int j=i;j<=S/2;j++)
b[j]+=c[j-i];
c=b;
}
cout<<c[S/2]/2;
}
// Meet in the middle 652ms
#include <bits/stdc++.h>
using namespace std;
using ld=long long;
ld n,S,ans;
map<int,ld> m;
void dp(int i,int k,int e) {
if (i==e) {
if (e==n) ans+=m[S-k];
else m[k]++;
return;
}
dp(i+1,k,e);
dp(i+1,k+i,e);
}
int main() {
cin>>n;
S=n*(n+1)/2;
if (S%2==0) {
S/=2;
dp(1,0,n/2);
dp(n/2,0,n);
}
cout<<ans;
}
728x90
'백준브실골 > DP' 카테고리의 다른 글
백준 22886, Moons and Umbrellas (0) | 2023.07.16 |
---|---|
백준 25097, Controlled Inflation (0) | 2023.07.16 |
백준 12783, 곱셈 게임 (0) | 2023.05.11 |
백준 2031, 이 쿠키 달지 않아! (0) | 2023.04.05 |
백준 21757, 나누기 (0) | 2023.03.30 |
백준 26156, 나락도 락이다 (0) | 2023.03.29 |
백준 24466, 도피 (0) | 2023.03.28 |
백준 10109, Troyangles (0) | 2023.03.28 |
댓글