背包问题 模版体形汇总

题目总链接

01 背包问题

分析

  • 题意

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 vi, 价值是 Wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围 0 <N, VS 1000
0 < Vi, Wi S 1000
输入样例
45
12
24
34
45

  • 分析:注意 dp 的方向和 dp 时候 for 循环的范围

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;

int dp[mxn];

int main()
{
    /* fre(); */
    int n, m;
    sc("%d %d", &n, &m);

    int v, w;
    for_(i, 1, n)
    {
        sc("%d %d", &v, &w);
        rep_(j, m, v)
            dp[j] = max(dp[j], dp[j - v] + w);
    }

    pr("%d\n", dp[m]);

    return 0;
}

完全背包问题

分析

  • 题意

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 Vi, 价值是 Wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围 0<N.V<10000<Vi, WiS 1000
输入样例
4 5
1 2
24
34
45
输出样例:8

  • 分析
  1. 从后往前往 for 循环 dp,这样可以重复利用 当前正在讨论的物品

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;

int dp[mxn];

int main()
{
    /* fre(); */
    int n, m;
    sc("%d %d", &n, &m);

    int v, w;
    for_(i, 1, n)
    {
        sc("%d %d", &v, &w);
        for_(j, v, m)
            dp[j] = max(dp[j], dp[j - v] + w);
    }

    pr("%d\n", dp[m]);

    return 0;
}

多重背包问题 I

分析

  • 题意

有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 s; 件,每件体积是 Vi, 价值是 Wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 1 种物品的体积、价值和数量
输出格式
输出一个整数,表示最大价值
数据范围 0<N, VS 100
0<Vi, W; si 100
输入样例
4 5
1 2 3
2 41
3 4 3
452
输出样例:
10

  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[mxn];

int main()
{
    /* fre(); */
    int n, m;
    sc("%d %d", &n, &m);

    int v, w, s;
    for_(i, 1, n)
    {
        sc("%d %d %d", &v, &w, &s);
        for_(j, 1, s)
        {
            rep_(k, m, v)
                dp[k] = max(dp[k], dp[k - v] + w);
        }
    }

    pr("%d\n", dp[m]);

    return 0;
}




多重背包问题 II

分析

  • 题意

有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi, 价值是 Wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
ONS 10000<VS20000<Vi, Wi, si 2000
提示:
本题考查多重背包的二进制优化方法。
输入样例
45
123
241
343
45 2
输出样例:

  • 分析
  1. 利用二进制拆分的思想,任何一个数都可,拆分一些 2 的次方数的和(最后一个拆分剩下的数 可能不是 2 某个次方数)

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[mxn];

int n, m;
int v, w, s;
void sov(int t)
{
    rep_(j, m, v * t)
        dp[j] = max(dp[j], dp[j - v * t] + t * w);
}

int main()
{
    /* fre(); */
    sc("%d %d", &n, &m);

    for_(i, 1, n)
    {
        sc("%d %d %d", &v, &w, &s);

        int t = 1;
        while(s >= t)
        {
            s -= t;
            sov(t);
            t <<= 1;
        }

        if(s) sov(s);
    }

    pr("%d\n", dp[m]);

    return 0;
}


多重背包问题 III

分析

  • 题意

有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 Vi, 价值是 Wis
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V (0 <N< 1000,0 <V< 20000), 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围 0<N<1000
0<V<200000<Vi, Wi, Si S 20000
提示
本题考查多重背包的单调队列优化方法。
输入样例
45
123
241
343
4 5 2
输出样例:

  • 分析
  1. 利用单调队列来维护一个极大值队列

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[2][mxn];
int q[mxn];             //注意这里 我们维护的是一个 极大值的单调队列, 
#define cal(x, i) dp[x ^ 1][q[i]] + (k - q[i]) / v * w

int main()
{
    /* fre(); */
    int n, m;
    sc("%d %d", &n, &m);

    int v, w, s;
    
    int x = 0;          //滚动数组,用的标号
    for_(i, 1, n)
    {
        sc("%d %d %d", &v, &w, &s);

        x ^= 1;
        for_(j, 0, v - 1)           //枚举余数j,j + x * v(就能表示出所有的以j为余数的数), 这样当我们依次枚举了 v的所有余数j之后,就能表示出所有的可能的数(指的是:在(1~s)*v之间的数)了
        {
            int hh = 0, tt = -1;
            for(int k = j; k <= m; k += v)          //枚举出来 可能的数
            {
                while(tt >= hh && (k - q[hh]) / v > s) hh ++;       //排除太远,越界的

                if(tt >= hh) dp[x][k] = max(dp[x ^ 1][k], cal(x, hh));

                while(tt >= hh && dp[x ^ 1][q[hh]] - (q[hh] - j) / v * w <= dp[x ^ 1][k] - (k - j) / v * w) tt --;      //消除距离产生的影响

                q[++ tt] = k;
            }
        }
    }

    pr("%d\n", dp[x][m]);

    return 0;
}

混合背包问题

分析

  • 题意
    有 N 种物品和一个容量是 V 的背包。
    物品一共有三类:
    ・第一类物品只能用 1 次 (01 背包) ;
    , 第二类物品可以用无限次 (完全背包) ;
    , 第三类物品最多只能用 si 次 (多重背包) ;
    每种体积是 Vi, 价值是 Wi
    求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
    输出最大价值。
    输入格式
    第一行两个整数,N,V, 用空格隔开,分别表示物品种数和背包容积。
    接下来有 N 行,每行三个整数 vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
    .Si=-1 表示第 i 种物品只能用 1 次;
    .Si=0 表示第 i 种物品可以用无限次.Si>0 表示第 i 种物品可以使用 si 次;
    输出格式
    输出一个整数,表示最大价值。
    数据范围 0<N, VS 1000
    0<Vi, Wi S 1000
    -1 SSi S 1000
    输入样例
    输入样例
    4 5
    1 2 -1
    2 4 1
    340
    4 5 2
    输出样例:в
  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[mxn];
int n, m;
int v, w, s;
void sov(int t)
{
    rep_(j, m, t * v) dp[j] = max(dp[j], dp[j - t * v] + t * w);
}

int main()
{
    /* fre(); */

    sc("%d %d", &n, &m);
    for_(i, 1, n)
    {
        sc("%d %d %d", &v, &w, &s);
        if(s == 0)
        {
            for_(j, v, m) dp[j] = max(dp[j], dp[j - v] + w);
        }
        else
        {
            if(s == -1) s = 1;

            int t = 1;
            while(s >= t)           //对01背包进行、对多重背包 进行二进制拆分
            {
                s -= t;
                sov(t);
                t <<= 1;
            }

            if(s) sov(s);
        }
    }

    pr("%d\n", dp[m]);

    return 0;
}

二维费用的背包问题

分析

  • 题意

有 N 件物品和一个容量是 V 的背包,背包能承受的最大量量是 M。
每件物品只能用一次。体积是 Vi, 重量是 mi, 价值是 Wi
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N , V,M, 用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。
接下来有 N 行,每行三个整数 Vi, mi;, Wi, 用空格隔开,分别表示第 i 件物品的体积、重量和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N S 1000 0<V,M S100 0<Vimi< 100 0<WiS1000
输入样例
456
123
244
345
15 6
输出样例:

  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e3 + 5;
int dp[mxn][mxn];
int N, M, V;
int v, m, w;


int main()
{
    /* fre(); */
    sc("%d %d %d", &N, &V, &M);
    for_(i, 1, N)
    {
        sc("%d %d %d", &v, &m, &w);
        rep_(j, V, v)
        {
            rep_(k, M, m)
                dp[j][k] = max(dp[j][k], dp[j - v][k - m] + w);
        }
    }

    pr("%d\n", dp[V][M]);

    return 0;
}

分组背包问题

分析

  • 题意
    有 N 组物品和一个容量是 V 的背包。
    每组物品有若干个,同一组内的物品最多只能选一个。
    每件物品的体积是 Vj, 价值是 Wi, 其中 i 是组号,j 是组内编号。
    求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
    输出最大价值。
    输入格式
    第一行有两个整数 N, V, 用空格隔开,分别表示物品组数和背包容量。
    接下来有 N 组数据
    ・每组数据第一行有一个整数 Si, 表示第 i 个物品组的物品数量;
    ・每组数据接下来有 Si 行,每行有两个整数 vj, wj, 用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
    输出格式
    输出一个整数,表示最大价值。
    数据范围
    <N, Vs 1000<Si<100<Vij, Wij S 100
    输入样例
    35
    2
    12
    :
  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 105;
int n, m;
int dp[mxn];
int v[mxn][mxn], w[mxn][mxn], s[mxn];

int main()
{
    /* fre(); */
    sc("%d %d", &n, &m);
    for_(i, 1, n)
    {
        sd(s[i]);
        for_(j, 1, s[i])
            sc("%d %d", &v[i][j], &w[i][j]);
    }

    for_(i, 1, n)
        rep_(j, m, 0)
        {
            for_(k, 1, s[i])
                if(j >= v[i][k]) dp[j] = max(dp[j], dp[j - v[i][k]] + w[i][k]);
        }

    pr("%d\n", dp[m]);


    return 0;
}

有依赖的背包问题

分析

  • 题意
    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
    第 i 件物品的体积是 vi, 价值是 Wi
    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
    输出最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。
    输入格式
    第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积
    接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
    输出格式
    输出一个整数,表示方案数模 109+7 的结果
    数据范围 0<N, VS 1000
    0<Vi, Wi s 1000
    输入样例
    45
    12
    2 4
    34
    4 6
    输出样例:2
  • 分析

代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 110;
int n,m;
int h[N],e[N],ne[N],idx;
/*h数组是邻接表的头它的下表是当前节点的标号,值是当前结点第一条边的编号(其实是最后加入的那一条边),e数组是边的集合,它的下标是当前边的编号,数值是当前边的终点;
  ne是nextedge,如果ne是-1表示当前结点没有下一条边,ne的下标是当前边的编号,数值是当前结点的下一条边的编号,idx用于保存每一条边的上一条边的编号。
  这样我们就知道了当前结点的第一条边是几,这个边的终点是那个结点,该节点的下一条边编号是几,那么邻接表就完成了
  */ 
int v[N],w[N],f[N][N]; 

void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;//该方法同于向有向图中加入一条边,这条边的起点是a,终点是b,加入的这条边编号为idx 
}

void dfs(int u){
    for(int i = h[u];i!=-1;i = ne[i]){//对当前结点的边进行遍历 
        int son = e[i];//e数组的值是当前边的终点,即儿子结点 
        dfs(son); 
        for(int j = m-v[u];j>=0;j--){
            //在做这一题之前,我们首先应该明白,我们正在讨论的 树中的某个节点x为根的整个子树,就相当于一个 分组背包问题,所以树中有多少个节点就是多少个分组背包问题
            //遍历背包的容积,因为我们是要遍历其子节点,所以当前节点我们是默认选择的。
            //这个时候当前结点我们看成是分组背包中的一个组,"当前正在讨论的子节点son" 的每一种选择我们都看作是组内一种物品(这里的”每一种son的每一种选择“指的是 ”每种选择造成的dp[son][1~m]最优值“);
            //从大到小遍历:(举个例子来说为什么这样?,在01背包中的第二从for循环 “从大到小遍历”, 是为了 把dp[][]的二维空间优化成一位空间dp[]; 而在这一题中表面上看:f[][] 是一个二维数组,但是我们要注意到它的第一纬度的“下标”只是起到表示这个子树是第几个分组背包问题)
            //我们每一次都默认选择当前结点,因为到最后根节点是必选的。 
            for(int k = 0;k<=j;k++){//去遍历子节点的组合 
                f[u][j] = max(f[u][j],f[u][j-k]+f[son][k]);
            }
        }
    }
    //加上刚刚默认选择的父节点价值
    for(int i = m;i>=v[u];i--){
        f[u][i] = f[u][i-v[u]]+w[u];
    }
    //因为我们是从叶子结点开始往上做,所以如果背包容积不如当前物品的体积大,那就不能选择当前结点及其子节点,因此赋值为零 
    for(int i = 0;i<v[u];i++){
        f[u][i] = 0;
    }
}

int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m;
    int root;
    for(int i = 1;i<=n;i++){
        int p;
        cin>>v[i]>>w[i]>>p;
        if(p==-1){
            root = i;
        }else{
            add(p,i);//如果不是根节点就加入邻接表,其中p是该节点的父节点,i是当前是第几个节点
        }
    }
    dfs(root);
    cout<<f[root][m]<<endl;
    return 0;
}


      

背包问题求最优解方案数

分析

  • 题意

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 Vi, 价值是 Wi.
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示方案数模 109+7 的结果。
数据范围
0<N, Vs 1000
0<Vi, Wi S 1000
输入样例
4 5
1 2
24
34
46
输出样例:

  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1005;
int dp[mxn];
int cnt[mxn];

int main()
{
    /* fre(); */
    int n, m;
    int v, w;
    sc("%d %d", &n, &m);

    for_(i, 0, m) cnt[i] = 1;           //注意这里为什么要 cnt 中的元素全部初始化为1,因为最终方案结果不必填满背包

    for_(i, 1, n)
    {
        sc("%d %d", &v, &w);
        rep_(j, m, v)
        {
            int val = dp[j - v] + w;
            if(val > dp[j])
            {
                dp[j] = val;
                cnt[j] = cnt[j - v];
            }
            else if(val == dp[j])
            {
                cnt[j] += cnt[j - v];
                cnt[j] %= mod;
            }
        }
    }
    pr("%d\n", cnt[m]);


    return 0;
}

背包问题求具体方案 / 记录最优解物品选择的路径

分析

  • 题意

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 vi, 价值是 Wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1… N.
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。
物品编号范围是 1… N。
数据范围
0<N, V< 1000
0<Vi, Wi S1000
输入样例 4
输出样例:

  • 分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1005;
int dp[mxn][mxn];
int n, m;
int v[mxn], w[mxn];

int main()
{
    /* fre(); */
    sc("%d %d", &n, &m);
    for_(i, 1, n)
        sc("%d %d", &v[i], &w[i]);

    rep_(i, n, 1)
    {
        for_(j, 0, m)
        {

            dp[i][j] = dp[i + 1][j];
            if(j >= v[i])               //分类讨论不要忘了!!!
                dp[i][j] = max(dp[i][j], dp[i + 1][j - v[i]] + w[i]);
        }
    }

    int sum_v = m;
    for_(i, 1, n)
    {
        //对最后一个物品进行特判
        if(i == n && sum_v >= v[i])
        {
            pr("%d ", i);
            break;
        }

        if(sum_v <= 0) break;

        if(sum_v >= v[i] && dp[i][sum_v] == dp[i + 1][sum_v - v[i]] + w[i])
        {
            sum_v -= v[i];
            pr("%d ", i);
        }
    }

    return 0;
}

背包问题第k优解

题目链接

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1010;
int f[mxn][mxn];
int n, m, k;
int v[mxn], w[mxn];
int a[mxn], b[mxn];

int main()
{
    /* fre(); */
    int T; sd(T);   
    while(T --)
    {
        sc("%d %d %d", &n, &m, &k);

        for_(i, 1, n) sd(w[i]);
        for_(i, 1, n) sd(v[i]);

        memset(f, 0, sizeof f);

        for_(i, 1, n)
        {
            rep_(j, m, v[i])
            {
                for_(l, 1, k)           //处理两种情况的状态转移方程分别存储到a、b数组中
                {
                    a[l] = f[j][l];
                    b[l] = f[j - v[i]][l] + w[i];
                }

                a[k + 1] = b[k + 1] = -1;       //结束标志
                int x = 1, y = 1, z = 1;
                while((x <= k || y <= k) && z <= k)         //注意让求的第k大是不是严格第k大
                {
                    if(a[x] > b[y])
                    {
                        f[j][z] = a[x ++];
                    }
                    else
                    {
                        f[j][z] = b[y ++];
                    }
                    if(f[j][z] != f[j][z - 1]) z ++;        //非严格第k大 只有不想等 z 才++
                }
            }
        }

        pr("%d\n", f[m][k]);

    }




    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值