You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
python/TangDou/AcWing/BeiBao/【总结】方案数-空间至多j.md

6.7 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

背包问题-方案数-空间至多j

一、01背包

例子:给你一堆物品,每个物品有一定的体积,每个物品只能选一个,求总体积 不超过m方案数

求方案数,没有价值累加的概念。

输入

//物品个数 体积
//每个物品的体积
4 5
2 2 3 7

输出

7

理解一下

一个都不选
a[0]
a[1]
a[2]
a[0]+a[1]
a[0]+a[2]
a[1]+a[2]

1、二维

#include <bits/stdc++.h>

using namespace std;
const int N = 110;

int n, m;
int f[N][N];//第一维前i个物品第二维体积j及以下

int main() {
    scanf("%d %d",&n,&m);
    //初始化,在前0个物品中选所有合法空间都有一种结果就是啥也不要啥也不要也满足“不超过j”这个条件方案数=1。
    for (int i = 0; i <= m; i++) f[0][i] = 1;

    for (int i = 1; i <= n; i++) {
        int v;
        scanf("%d",&v);
        for (int j = 0; j <= m; j++) {
            f[i][j] = f[i - 1][j];//不被i件物品
            if (j >= v) f[i][j] += f[i - 1][j - v];//能装下就要加上可以转移过来状态的方案数
        }
    }
    printf("%d\n",f[n][m]);
    return 0;
}

2、一维

#include <bits/stdc++.h>

using namespace std;
const int N = 110;

int n, m;
int f[N];

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 0; i <= m; i++) f[i] = 1;

    for (int i = 1; i <= n; i++) {
        int v;
        scanf("%d",&v);
        for (int j = m; j >= v; j--) f[j] += f[j - v];
    }
    printf("%d\n",f[m]);
    return 0;
}

二、完全背包

例子:给你一堆物品,每个物品有一定的体积,每个物品可以选无数多个求总体积不超过m方案数

输入

3 5
2 3 7

输出

5

理解一下:

一个都不选
a[0]
a[0]*2
a[1]
a[0]+a[1]

5种方案。

1、二维

#include <bits/stdc++.h>

using namespace std;
const int N = 110;

int n, m;
int f[N][N];

int main() {
    //文件输入
    freopen("ZhiDuo_WQ.in", "r", stdin);

    scanf("%d %d", &n, &m);
    for (int i = 0; i <= m; i++) f[0][i] = 1; //前0种物品中选择最多不超过m的体积下选择的方案只有一种合法就是啥也不选
    for (int i = 1; i <= n; i++) {
        int v;
        scanf("%d", &v);
        /*写法1*/
        for (int j = 0; j <= m; j++) {
            if (j >= v)
                f[i][j] = f[i - 1][j] + f[i][j - v];
            //注意这里与01背包的差别
            // 在01背包的状态转移中i就是i,它之前不能出现过i,只能从i-1转移
            // 在完全背包的状态转移中i出现过没关系只要能装下出现几个都行可以从i转移
            // 01背包
            // f[i][j] = f[i - 1][j] + f[i - 1][j - v];
            else
                f[i][j] = f[i - 1][j];
        }

        /*写法2*/
        // for (int j = 0; j <= m; j++) {
        //     f[i][j] = f[i - 1][j];
        //     if (j >= v) f[i][j] += f[i][j - v];
        // }
    }
    printf("%d\n", f[n][m]);
    return 0;
}

2、一维

#include <bits/stdc++.h>

using namespace std;
const int N = 110;

int n, m;
int f[N];

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 0; i <= m; i++) f[i] = 1;
    for (int i = 1; i <= n; i++) {
        int v;
        scanf("%d",&v);
        //完全背包
        for (int j = v; j <= m; j++) f[j] += f[j - v];
        //对比01背包
        //for (int j = m; j >= v; j--) f[j] += f[j - v];
    }
    printf("%d\n",f[m]);
    return 0;
}

练习题 洛谷 P1832

欧筛下标从1开始

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 1e3 + 5;

LL f[N][N];

//欧拉筛,下标从1开始
//这个欧拉筛黄海修改过修改下标从1开始
int primes[N], st[N], cnt;
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[++cnt] = i;
        for (int j = 1; primes[j] * i <= n; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int main() {
    int m;
    scanf("%d", &m);
    get_primes(m); // 1 ~ m以内有cnt个素数

    int n = cnt;
    for (int i = 0; i <= n; i++) f[i][0] = 1; //初始化如果刚好只可以选一个数即j == primes[i],那么只有一种方案。

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            /*
            // j足够大那么对于一个素数可以选或是不选。两种方案要求总和。
            if (j >= primes[i])
                f[i][j] = f[i - 1][j] + f[i][j - primes[i]];
            else
                f[i][j] = f[i - 1][j]; // j不够大只有不选这一种方案。
            */

            //与上面的代码是等价的一样可以AC
            f[i][j] = f[i - 1][j];
            if (j >= primes[i]) f[i][j] += f[i][j - primes[i]];
        }
    }
    printf("%lld\n", f[n][m]);
    return 0;
}

欧筛下标从0开始

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 1e3 + 5;

LL f[N][N];

//欧拉筛
int primes[N], st[N], cnt;
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] * i <= n; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int main() {
    int m;
    scanf("%d", &m);
    get_primes(m); // 1 ~ m以内有cnt个素数

    int n = cnt;
    for (int i = 0; i <= n; i++) f[i][0] = 1; //初始化如果刚好只可以选一个数即j == primes[i],那么只有一种方案。

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            /*
            // j足够大那么对于一个素数可以选或是不选。两种方案要求总和。
            if (j >= primes[i])
                f[i][j] = f[i - 1][j] + f[i][j - primes[i]];
            else
                f[i][j] = f[i - 1][j]; // j不够大只有不选这一种方案。
            */

            //与上面的代码是等价的一样可以AC
            f[i][j] = f[i - 1][j];
            if (j >= primes[i - 1]) f[i][j] += f[i][j - primes[i - 1]];
        }
    }
    printf("%lld\n", f[n][m]);
    return 0;
}