题意:
给定
n
n
n个工作的工作时间
t
a
s
k
[
i
]
task[i]
task[i]以及一天内可以进行工作的时间
s
e
s
s
i
o
n
T
i
m
e
sessionTime
sessionTime,问最少用几天可以完成给定的
n
n
n个工作。
数据范围:
1
≤
n
≤
14
1\leq n\leq 14
1≤n≤14
1
≤
t
a
s
k
[
i
]
≤
10
1\leq task[i] \leq 10
1≤task[i]≤10
1
≤
m
a
x
{
t
a
s
k
[
i
]
}
≤
15
1\leq max\{task[i]\}\leq 15
1≤max{task[i]}≤15
题解:
本题的数据范围一看很小。
赛时先是看错数据范围全排列枚举超时,然后考虑枚举乱搞过了就没管了。
今天发现FST了,所以来记录下正解,参考了官方题解。
这个数据范围不管怎么想一定是指数级别的,考虑状压。
f
[
i
]
f[i]
f[i]表示前状态为
i
i
i时,需要完成状态
i
i
i的所有工作的最少天数,答案显然是
f
[
2
n
−
1
]
f[2^n-1]
f[2n−1]
考虑下状态转移:例如
f
[
7
]
f[7]
f[7],状态为
111
111
111,可以从
f
[
0
]
,
f
[
1
]
,
f
[
2
]
,
f
[
3
]
,
f
[
4
]
,
f
[
5
]
,
f
[
6
]
f[0],f[1],f[2],f[3],f[4],f[5],f[6]
f[0],f[1],f[2],f[3],f[4],f[5],f[6]转移而来。
这些状态中两个不同的状态多余的部分可能是可以合并成一天完成的,因此不仅需要考虑完成天数,还需要考虑当前状态的冗余部分。
f
[
i
]
f[i]
f[i]状态表示用一个
p
a
i
r
pair
pair记录,更新为状态为
i
i
i时的完成天数
f
[
i
]
.
f
i
r
s
t
f[i].first
f[i].first,以及当前完成的最后一天已经用了的时间。
状态转移为:
f
[
i
]
=
m
i
n
(
f
[
i
]
,
a
d
d
(
f
[
i
−
2
j
]
+
w
[
j
]
)
)
f[i]=min(f[i],add(f[i-2^j]+w[j]))
f[i]=min(f[i],add(f[i−2j]+w[j]))
其中
a
d
d
add
add函数为:
auto add = [&](const pair<int, int> &o, int x) -> pair<int, int> {
if(o.second + x <= sessionTime) return {o.first, o.second + x};
return {o.first + 1, x};
};
代码:
class Solution {
public:
int minSessions(vector<int>& tasks, int sessionTime) {
const int INF = 0x3f3f3f3f;
int n = tasks.size();
vector<pair<int, int>> f(1 << n, {INF, INF});
f[0] = {1, 0};
auto add = [&](const pair<int, int> &o, int x) -> pair<int, int> {
if(o.second + x <= sessionTime) return {o.first, o.second + x};
return {o.first + 1, x};
};
for(int i = 1; i < 1 << n; ++i)
for(int j = 0; j < n; ++j)
if(i >> j & 1)
f[i] = min(f[i], add(f[i ^ (1 << j)], tasks[j]));
return f[(1 << n) - 1].first;
}
};