DP(状压专题十四)

23 篇文章 0 订阅
20 篇文章 0 订阅

题意: 给定n头牛, 每头牛有叁种属性, 分别是身高,体重和力量, 力量指的是一头牛最大能承受叠在他上方的牛的重量和. 现给定一个高度 和 牛的集合, 问在牛能组成的高度大于给定高度的状态中的最大的力量值是多少.

写在前面: 做了这么多状压dp, 我总结出状压dp的两种转移方式, 第一种,枚举未知让已知推, 这种写法比较适合,答案是基于当前状态的dp(就比如说底下这题, 只有高度比给定高度高的状态才能对答案做出贡献), 但是这种dp对边界条件要求比较严格, 其默认有已知状态, 而初始的已知状态是我们赋予的边界条件, 所以边界条件一定要慎之又慎. 另一种是枚举已知推未知, 这种推法和第一种推法本质上是一样的, 只不过写法要简单点, 逻辑更清晰. 以上两种做法没有孰优孰劣之分, 应该根据不同的题目特点, 选择合适的写法

>> face <<

Strategy:状压DP

状态: dp[i]->状态为i时的最大重量

目标: d p [ i ∈ h [ i ] &gt; = H ] ∣ m a x dp[i\in{h[i] &gt;= H}]\mid_{max} dp[ih[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 &lt; &lt; j − 1 ] − w [ j ] , s [ j ] ) ) dp[i] = max(dp[i], min(dp[i\bigoplus1&lt;&lt;j-1] - w[j], s[j])) dp[i]=max(dp[i],min(dp[i1<<j1]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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值