钉耙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问题的处理

浙公网安备 33010602011771号