고급 가이드 Acwing 알고리즘 연구 노트는 주제를 DP : 배낭 문제

\ (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;
}

추천

출처www.cnblogs.com/zxytxdy/p/12161006.html