题意: 给定n头牛, 每头牛有叁种属性, 分别是身高,体重和力量, 力量指的是一头牛最大能承受叠在他上方的牛的重量和. 现给定一个高度 和 牛的集合, 问在牛能组成的高度大于给定高度的状态中的最大的力量值是多少.
写在前面: 做了这么多状压dp, 我总结出状压dp的两种转移方式, 第一种,枚举未知让已知推, 这种写法比较适合,答案是基于当前状态的dp(就比如说底下这题, 只有高度比给定高度高的状态才能对答案做出贡献), 但是这种dp对边界条件要求比较严格, 其默认有已知状态, 而初始的已知状态是我们赋予的边界条件, 所以边界条件一定要慎之又慎. 另一种是枚举已知推未知, 这种推法和第一种推法本质上是一样的, 只不过写法要简单点, 逻辑更清晰. 以上两种做法没有孰优孰劣之分, 应该根据不同的题目特点, 选择合适的写法
>> face <<
Strategy:状压DP
状态: dp[i]->状态为i时的最大重量
目标: d p [ i ∈ h [ i ] > = H ] ∣ m a x dp[i\in{h[i] >= H}]\mid_{max} dp[i∈h[i]>=H]∣max所有比给定高度高的最大贡献
边界: d p [ 0 ] = ∞ dp[0] = \infty dp[0]=∞ 转移时根据上一状态的承受力减去新牛重量和新牛的力量里面取min,所以第一头牛就第一头牛的重量(min(dp[0] - w[i], s[1]))(承受力不能为负数)
合法判断: 本题无
转移方程:
d p [ i ] = m a x ( d p [ i ] , m i n ( d p [ i ⨁ 1 < < j − 1 ] − w [ j ] , s [ j ] ) ) dp[i] = max(dp[i], min(dp[i\bigoplus1<<j-1] - w[j], s[j])) dp[i]=max(dp[i],min(dp[i⨁1<<j−1]−w[j],s[j]))
attention: 边界与逻辑
双倍经验:
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
using namespace std;
const int maxn = 1e5 + 10;
const int mod = 9999973;
ll n, H, h[maxn], w[maxn], s[maxn], dp[1 << 20];
int main()
{
ios::sync_with_stdio(0);
cin >> n >> H;
_rep(i, 1, n)
{
cin >> h[i] >> w[i] >> s[i];
}
dp[0] = oo;
ll ans = 0;
_for(i, 1, 1 << n)
{
int hh = 0;
_rep(j, 1, n)
{
if (i >> j - 1 & 1)
{
hh += h[j];
dp[i] = max(dp[i], min(dp[i ^ 1 << j - 1] - w[j], s[j]));
}
}
if(hh >= H){
ans = max(ans, dp[i]);
}
}
// cout << (ans == 0 ? "Mark is too tall" : ans) << endl;
printf(ans == 0? "Mark is too tall": "%lld", ans);
}