01背包变种

Posted on 2023-03-01 23:22  lyc2002  阅读(8)  评论(0编辑  收藏  举报

[acwing]1047.糖果

/*
    dp[i][j] 表示只考虑前 i 件物品,模 k 余 j 的最大价值
    
    如果不取第 i 件物品,dp[i][j] = dp[i - 1][j]
    如果取第 i 件物品,dp[i][j] = dp[i - 1][((j - v[i]) % k + k) % k] + v[i]
    
    遍历顺序 i[1, n],j[0, k - 1]
    
    无需初始化
    
    dp[n][0]
*/
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 110;

int n, k;
int v[N];
int dp[N][N];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
    
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= k - 1; j++) {
            dp[i][j] = dp[i - 1][j]; // 不取第 i 个物品
            if (dp[i - 1][((j - v[i]) % k + k) % k] != 0) // 取第 i 个物品,看之前有没有值与 v[i] 相加模 k 等于 j
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][((j - v[i]) % k + k) % k] + v[i]);
            else if (v[i] % k == j) // 如果找不到,说明不能在原来的基础上进一步增大,再判断只有 v[i] 自身能否满足模 k 等于 j
                dp[i][j] = max(dp[i - 1][j], v[i]);
        }
        
    printf("%d", dp[n][0]);
    
    return 0;
}

[acwing]1234.倍数问题

/*
    这道题结合了 [acwing]1047.糖果 和 [acwing]1050.鸣人的影分身

    dp[i][j][k] 表示只考虑前 i 个物品,其总价值模 m 余 j,且个数为 k 的最大价值
    
    如果不选第 i 个物品,dp[i][j][k] = dp[i - 1][j][k]
    如果选第 i 个物品,dp[i][j][k] = dp[i - 1][((j - v[i]) % m + m) % m][k - 1] + v[i]
    
    遍历顺序 i[1, n],j[0, m - 1],k[0, 3]
    
    无需初始化
    
    目标 dp[n][0][3]
    
    由于 n 过大会爆内存,需优化
*/
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010, M = 1010;

int n, m;
int v[N];
int dp[N][M][4];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
    
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < m; j++)
            for (int k = 0; k <= 3; k++) {
                int &u = dp[i][j][k];
                u = dp[i - 1][j][k];
                if (k - 1 >= 0) {
                    if (dp[i - 1][((j - v[i]) % m + m) % m][k - 1] > 0)
                        u = max(u, dp[i - 1][((j - v[i]) % m + m) % m][k - 1] + v[i]);
                    else if (v[i] % m == j)
                        u = max(u, v[i]);
                }
                
            }
            
    printf("%d", dp[n][0][3]);
    
    return 0;
}
/*
	mod[i] 存模 m 余 i 的数
	新的 v 数组只存模 m 余 i 前三大的数
*/
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010, M = 1010;

int n, m;
int v[N], idx = 1;
vector<int> mod[M];
int dp[3 * M][M][4];

bool cmp(const int &lhs, const int &rhs)
{
    return lhs > rhs;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &v[i]);
        mod[v[i] % m].push_back(v[i]);
    }
    for (int i = 0; i < m; i++) sort(mod[i].begin(), mod[i].end(), cmp);
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < 3 && j < mod[i].size(); j++)
            v[idx++] = mod[i][j];
    }
    
    for (int i = 1; i <= idx - 1; i++)
        for (int j = 0; j < m; j++)
            for (int k = 0; k <= 3; k++) {
                int &u = dp[i][j][k];
                u = dp[i - 1][j][k];
                if (k - 1 >= 0) {
                    if (dp[i - 1][((j - v[i]) % m + m) % m][k - 1] > 0)
                        u = max(u, dp[i - 1][((j - v[i]) % m + m) % m][k - 1] + v[i]);
                    else if (v[i] % m == j)
                        u = max(u, v[i]);
                }
                
            }
            
    printf("%d", dp[idx - 1][0][3]);
    
    return 0;
}