\ (Acwing \) 알고리즘 고급 학습 \ (DP \) 주제 : 배낭 문제
배낭 문제 :
- 배낭 문제는 선형 \ (DP \) 모델의 아주 특별한 종류.
- 01 배낭 : 제품은 한 번만 사용할 수 있습니다.
- 전체 배낭 : 항목은 제한 시간을 사용할 수 있습니다.
- 여러 배낭 : 각 항목 제한을 가지고 있습니다.
- 배낭 그룹 : 각 그룹은 여러 그룹 중 하나를 선택할 수 있습니다.
. 실시 예 1 acw278 : 복합 디지털
질문의 의미 :
- 주어 (N \) \ 숫자, 숫자의 수를 선택하고 그들을 만드는 \ (m를 \) , 프로그램의 몇 가지를 물었다.
아이디어 :
- 01 배낭 문제.
\ (A_I \) 부피로서 \ (m \) 배낭 용량으로서 \ (F (J) \) 를 대신하여 대 \ (J \) 프로그램의 수.
전이 방정식 \ (F (J) = F + (JA (I)) \) .
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e2+10;
const int maxm = 1e4+10;
int a[maxn], n, m, f[maxn];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
f[0] = 1;
for(int i = 1; i <= n; i++)
for(int j = m; j >= a[i]; j--)
f[j] += f[j-a[i]];
cout << f[m] << endl;
return 0;
}
예 2 acw279 : 분할의 자연수
문제의 의미
- 천연 수에 \ N- (\) 은 필요 \ N- (\) 반복 디지털 조작에 참가할 수 있고, 정수의 형태로 여러 첨가제로 분할한다.
- 몇 가지 프로그램을 찾을 수 있습니다.
사고
- 전체 배낭, 숫자는 여러 번 사용할 수 있기 때문이다.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e3+10;
const ll mod = 2147483648;
ll f[maxn], n;
int main()
{
cin >> n;
f[0] = 1;
for(int i = 1; i <= n-1; i++)
for(int j = i; j <= n; j++)
f[j] = (f[j]+f[j-i])%mod;
f[n] = (f[n]+mod)%mod;
cout << f[n] << endl;
return 0;
}
예 3 acw280 : 심사 위원
문제의 의미
- 이 \ (N- \) 개인, 각 개인은이 개 점수가 \ (P (I), D (I) \) 군으로부터 선택 \ (m \) 찾아 그 프로그램 \ (| DP | \) 최소, 여러 종류 정당한 프로그램의 다음 선택 \ (d +의 피 \) 최대 기.
데이터 범위
- \ (200 당량 n \ 20 당량 m \, P, D \ 당량 20 \)
사고
- 아주 소수의 사람을 선출하지만, 점수 차이가 낮고, 그래서 할 수있는 일종의 첫 번째 프로그램의 차이에 의해 \ (- 400 \) 에 (+ 400 \) \ 점수 섹션.
- 그럼 우리의 차이에 갈 수 \ (0 \) 로 이동하지 않을 경우, 찾는 프로그램 (1 -1 \) \ 그런 계획은 없다 ...
- 따라서, 정의 \ (F (I, J, K)이 \) 전 나타내는 \ (I \) 개인 선택 \ (J \) 개인의 차이 \ (K \) \ (p + D를 \) 최대 .
- 각각만을 선택할 수 있습니다 사람이나 금지를 들어, 그래서 이것은 01 배낭입니다.
- 전송 식 (\ : F (난, J , K)이 최대 \ {F (I-1, J, K를 =), F (I, J-1, K-(p_i-d_i)) + (p_i + d_i) \} \) .
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200 + 10;
const int maxm = 800 + 10;
const int base = 400;
int n, m;
int f[maxn][25][maxm];
int p[maxn], d[maxn];
int ans[maxn];
int main()
{
int cnt = 1;
while(scanf("%d%d", &n, &m))
{
if(n==0 && m==0) break;
for(int i = 1; i <= n; i++)
scanf("%d%d", &p[i], &d[i]);
memset(f, -0x3f, sizeof(f));
f[0][0][base] = 0;
for(int i = 1; i <= n; i++)
for(int j = 0; j <= m; j++)
for(int k = 0; k < maxm; k++)
{
f[i][j][k] = f[i-1][j][k];
int tmp = k-(p[i]-d[i]);
if(tmp < 0 || tmp >= maxm) continue;
if(j < 1) continue;
f[i][j][k] = max(f[i][j][k], f[i-1][j-1][tmp]+p[i]+d[i]);
}
int v = 0;
while(f[n][m][base-v] < 0 && f[n][m][base+v] < 0) v++;
if(f[n][m][base-v] > f[n][m][base+v]) v = base - v;
else v = base + v;
int i = n, j = m, k = v, tot = 0;
while(j)
{
if(f[i][j][k] == f[i-1][j][k])// 可以不选第i个人
i--;
else
{
ans[++tot] = i; //选第i个人
k -= (p[i]-d[i]);
i--, j--;
}
}
int ansp = 0, ansd = 0;
for(int i = 1; i <= tot; i++)
{
ansp += p[ans[i]];
ansd += d[ans[i]];
}
printf("Jury #%d\n", cnt++);
printf("Best jury has value %d for prosecution and value %d for defence:\n", ansp, ansd);
for (int i = 1; i <= tot; i ++ )
printf(" %d", ans[i]);
puts(""); puts("");
}
return 0;
}
예 4 acw281 : 동전
문제의 의미
- 용 \ N- (\) 주화의 종류, \ (I \) 와 같은 교 (\ A_I) \ , 양 \ (C_I \) A, Q, Q 선택된 동전들로부터 \ (1 \) 로 \ (m \ ) 간격 액면가의 수에 직접 형성 할 수있다.
데이터 범위
- \ (100 당량 n \, m \ leq10 ^ 5 A_I \ leq10 ^ 5 C_I \ leq10 ^ 3 \) .
사고
- 여러 배낭 아무 문제가 없어야합니다.
- 여러 배낭 쓰기가 직접하지, 사실, 위험 바이너리가 큰 것에 대해 최적화 된 모노톤 큐 최적화 제어 할 수있을 것입니다 \ (10 ^ 7 \) 계산.
- 그러나 여기에서 주목해야 할 것입니다 :
- 이 문제는 단지 "가능성"하지 우려 수행에 대해 우려한다 "최적."
- 그는 긁어 수있는 경우 즉, 우리는 단지 우려하고있다.
- 집합 \ (F (I, J) \) 이전 나타내는 \ (I \) 번째 항목의 공칭 값 \ (J \) 이 조립 될 수 있는지를.
- 제 들어 \ (I \) 를 추가 할 수있는 주화의 유형 (j \) \ 두 가능한 액면이다.
- 미나토 좋은되었습니다.
- \ (JA (I) \) 미나토 그럼 \ (j \) 로부터는 \ (JA (I) \) 위에 옮긴다.
- 집합 \ (힘 (j) \) 나타내는 제 \ (I \) 상태가 구성 될 때 \ (j \) 방법 중 적어도 액면가 사용하는 \ (나는 \) 동전.
- 따라서 전송되도록 제, 이전 상태로 최대한 밖으로 긁어 때 \ (I \) 달성 할 수있는 주화 유형 "적어도"이러한 가능성.
- 즉, 첫 번째, 두 말을하는 것입니다 \ (I \) 상태 그의 경우 \ (true로 J는 == \) , 당신은 첫 번째 가질 수 없습니다 \ (I \) 동전의 종류.
- 만약에 \ (J \) 대신 \ (\ true로) 하지만, \ (F (JA (I) ) \ == true)를 충분히 동전이있을 때, 당신은 전송할 수 있습니다.
- 코드를 참조하십시오.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 10;
const int maxm = 1e5 + 10;
bool f[maxm];
int n, m;
int a[maxn], c[maxn];
int vis[maxm];
int main()
{
while(cin >> n >> m)
{
if(n == 0 && m == 0) break;
memset(f, 0, sizeof(f));
f[0] = true;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= m; j++) vis[j] = 0;
for(int j = a[i]; j <= m; j++)
if(!f[j] && f[j-a[i]] && vis[j-a[i]] < c[i]) {
f[j] = true;
vis[j] = vis[j-a[i]]+1;
}
} int ans = 0;
for(int i = 1; i <= m; i++)
ans += f[i];
cout << ans << endl;
}
return 0;
}