钉耙2025夏季第六场--cats的max--状态压缩DP(个人学习笔记)

常规想法按行和按列都不好转移。需要发现最终选择k行构成的结果为在每一行行中每行选取若干个(可能为0个)数值组成。设状态st为某一行被选取的数值的掩码,选取行的st对应数值和一定为所有行中st对应数值和最大,才能对答案贡献最大。不妨先将选取一行的所有状态的最大值求出dp[st],最终答案一定由dp中小于等于k个状态组成,接下来设f[i][st]为状态st由选取i个状态构成进行动态规划即可,f[k][(1<<m)-1]即为答案。

#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define endl '\n'

const int INF = 1e9 + 7;
const int MOD = 1e9 + 7;

struct Node
{
    int id;
    int val;

    bool operator<(const Node& other)
    {
        return this->val < other.val;
    }
};

int lowbit(int x)
{
    return x & (-x);
}

void solve()
{

    int n, m, k;
    cin >> n >> m >> k;

    vector<vector<int>> croods(n + 5, vector<int>(m + 5, 0));

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> croods[i][j];
        }
    }

    int ans = 0;
    if (k >= m)
    {
        for (int j = 1; j <= m; j++)
        {
            int maxn = 0;
            for (int i = 1; i <= n; i++)
            {
                maxn = max(maxn, croods[i][j]);
            }
            ans += maxn;
        }

        cout << ans << endl;
    }
    else
    {

        vector<int> dp((1ll << m), 0);

        map<int, int> lb_id;
        {
            int tmp = 1;
            for (int i = 0; i < m; i++)
            {
                lb_id[tmp] = i + 1;
                tmp *= 2;
            }
        }

        for (int i = 1; i <= n; i++)
        {
            vector<int> val((1ll << m));
            for (int st = 1; st <= (1ll << m) - 1; st++)
            {
                int sum = 0;

                sum = val[st - lowbit(st)] + croods[i][lb_id[lowbit(st)]];
                val[st] = sum;
                dp[st] = max(dp[st], sum);
            }
        }

        vector<vector<int>> f(k + 5, vector<int>((1ll << m), 0));

        for (int st = 1; st < (1ll << m); st++)
        {
            f[1][st] = dp[st];
        }
        for (int i = 2; i <= k; i++)
        {
            for (int st = 1; st < (1ll << m); st++)
            {
                for (int st2 = (st - 1) & st; st2; st2 = (st2 - 1) & st)
                {
                    //cout << "!" << st2 << endl;
                    f[i][st] = max(f[i][st], f[i - 1][st2] + dp[st - st2]);
                }
            }
        }

        cout << f[k][(1ll << m)-1] << endl;
    }



}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T = 1;

    cin >> T;
    for (int i = 1; i <= T; i++)
    {
        solve();
    }
}

收获:
加强对状态压缩DP问题的处理

posted @ 2025-08-06 15:47  青一凡  阅读(21)  评论(0)    收藏  举报