DP(状压专题十二)

26 篇文章 0 订阅
20 篇文章 0 订阅

题意: 有若干首曲子, 每首曲子具有三个性质, 名字 N i N_i Ni, 时长 c o s t i cost_i costi, 还有截止时刻 t i t_i ti,每首歌对答案的贡献是截止时刻减去播完该首曲子的时刻,现给定n首曲子, 演奏总时间, 最低胜利分数, 当且仅当, 播完n首曲子, 最终分数大于等于最低分 不输出 No answer, 其他情况输出最高分数和演奏顺序

>> face <<

Strategy:状压DP, i是一个表示二进制的十进制数,'1’代表该曲目已经被演奏过的状态的最大分数

状态: dp[i], 代表状态为i所能拿到的最大分数, ti[i]达到状态i的耗时全新的推法 (O(1)) 预处理

目标: dp[(1 << n) - 1] -> 全选的最高分

边界: 全部初始化为0,第一次转移主要由曲目得到

合法判断: 超时判断

转移方程:

d p [ i ] = m a x ( d p [ i ] , d p [ i 1 &lt; &lt; j − 1 ] + m a x ( 0 , q [ j ] . l e f t − t [ i ] ) ) , 不 是 t [ i j − 1 ] dp[i] = max(dp[i], dp[i ^ 1 &lt;&lt; j - 1] + max(0, q[j].left - t[i])) , 不是t[i ^ j -1] dp[i]=max(dp[i],dp[i1<<j1]+max(0,q[j].leftt[i])),t[ij1]

attention: 暂无

双倍经验: 这题有两个很大的的收获, 一个是方案输必须用逆序遍历, 另一个就是时间的推法, 日后定要回顾一下这题

#include <bits/stdc++.h>
#include <bits/extc++.h>
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define all(a) a.begin(), a.end()
#define met(a, b) memset(a, b, sizeof(a))
#define what_is(x) cout << #x << " is " << x << endl
#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 lowbit(x) x &(-x)
#define bin(x) cout << #x << " is " << bitset<sizeof(int) * 2>(x) << endl
#define pi acos(-1.0)
using namespace std;

const int maxn = 22;
const int mod = 1e8;

struct song
{
	string name;
	int cost, left;
} tmp;
int n, m, t, dp[1 << 22], ti[1 << maxn];
deque<song> q;
void plans(int i){//打印方案
	if(i == 0)return ;
	_rev(j, n, 1){//枚举状态i的最后一首歌,想想为什么是倒序?
		//bin(i);
		if(!(i >> j - 1 & 1 ))continue;
		if(max(0, q[j].left - ti[i]) + dp[i ^ 1 << j - 1] == dp[i]){
			//bin(i), bin(i ^ 1 << j - 1);
			//what_is(1 << j - 1);
			plans(i ^ 1 << j - 1);
			cout << q[j].name << endl;
			break;
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin >> n >> m >> t;
	_rep(i, 1, n)
	{
		cin >> tmp.name >> tmp.cost >> tmp.left;
		ti[1 << i - 1] = tmp.cost;
		q.push_back(tmp);
	}
	q.push_front(tmp);
	
	_rep(i, 0, (1 << n) - 1)
	{
		ti[i] = ti[i^lowbit(i)] + ti[lowbit(i)];
		if(ti[i] > t)
			continue;
		_rep(j, 1, n)
		{
			if (!(i >> j - 1 & 1)) //不在里面
				continue;
			int val = max(0, q[j].left - ti[i]);
			dp[i] = max(dp[i], dp[i ^ 1 << j - 1] + val);
		}
	}
	if (dp[(1 << n) - 1] >= m)
	{
		cout << dp[(1 << n) - 1] << endl;
		plans((1 << n) - 1);
	}
	else
		return puts("No Answer"), 0;
}

第一次回顾

  • time更新的方式很新颖, 大爱
  • 输出方案还是喜欢用我自己的方式
  • i, 1, j 三个东西注意点
#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 = (1 << 22);
const int mod = 1e6 + 3;
int n, m, t;
struct songs
{
	int cost, left;
	string name;
} q[25];
int dp[maxn], tim[maxn], bef[maxn], ans[maxn];
void print(int __){
	if(!__)return;
	print(bef[__]),cout << q[ans[__]].name << endl; 
}
int main()
{
	ios::sync_with_stdio(0);
	cin >> n >> m >> t;
	_rep(i, 1, n)
	{
		cin >> q[i].name >> q[i].cost >> q[i].left;
		tim[1 << i - 1] = q[i].cost;
	}
	_rep(i, 0, (1 << n) - 1)
	{
		tim[i] = tim[i ^ lowbit(i)] + tim[lowbit(i)];
		//what_is(tim[i]), what_is(i), what_is(tim[i ^ lowbit(i)]), what_is(tim[lowbit(i)]);
		//assert(tim[i] != 0 || i == 0);
		if (tim[i] > t)
			continue;
		_rep(j, 1, n)
		{
			if (i >> j - 1 & 1)
				continue;
			if (tim[i] + q[j].cost > t)
				continue;
			int val = max(0, q[j].left - tim[i] - q[j].cost);
			if (dp[i | 1 << j - 1] < dp[i] + val)
			{
				dp[i | 1 << j - 1] = dp[i] + val;
				bef[i | 1 << j - 1] = i;
				ans[i | 1 << j - 1] = j;
			}

			//dp[i | 1 << j - 1] = max(dp[i | 1 << j - 1], dp[i] + val);
		}
	}
	if (dp[(1 << n) - 1] < m)
	{
		cout << "No Answer" << endl;
	}
	else
	{
		cout << dp[(1 << n) - 1] << endl;
		print((1 << n) - 1);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值