题意: 有若干首曲子, 每首曲子具有三个性质, 名字 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 < < 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 << j - 1] + max(0, q[j].left - t[i])) , 不是t[i ^ j -1] dp[i]=max(dp[i],dp[i1<<j−1]+max(0,q[j].left−t[i])),不是t[ij−1]
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);
}
}