题意: T组测试数据。你有n个作业要做,每个作业有一个截止日期D和做完这个作业需要的天数C。一旦你开始做某个作业你就必须把它做完,不能同时做多个作业。每个作业延期完成1天就会产生1的不快乐值,问如何安排做作业顺序使得这个不快乐值最小,如果有多解,输出字典序最小的解。题目中为了方便,给作业的时候是按照字典序从小到大给出的。
思路:
dp[state]
d
p
[
s
t
a
t
e
]
表示作业完成状态为
state
s
t
a
t
e
的时候的最优解,
cost[state]
c
o
s
t
[
s
t
a
t
e
]
表示作业状态完成状态为
state
s
t
a
t
e
的时候的花的总天数。
state
s
t
a
t
e
是一个二进制数,
0≤state<2n
0
≤
s
t
a
t
e
<
2
n
。
state
s
t
a
t
e
状态可以由
pre
p
r
e
状态转移过来,当且仅当二进制下
pre
p
r
e
是
state
s
t
a
t
e
某个1变为0的得到的。
假设
pre
p
r
e
是
state
s
t
a
t
e
第
i
i
个1变为0的得到的,那么 状态转移到
i
i
状态的花费
那么状态转移方程为:
dp[state]=min(dp[state],dp[i]+temp),(0≤i<n)
d
p
[
s
t
a
t
e
]
=
m
i
n
(
d
p
[
s
t
a
t
e
]
,
d
p
[
i
]
+
t
e
m
p
)
,
(
0
≤
i
<
n
)
状态转移的同时要维护
cost[state]
c
o
s
t
[
s
t
a
t
e
]
,并记录路径(每一个状态是由哪来的)。
最终
dp[2n−1]
d
p
[
2
n
−
1
]
就是答案,然后在递归打印路径即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20;
const int INF = 0x3f3f3f3f;
char name[MAXN][105];
int n, c[MAXN], d[MAXN], dp[1<<20], cost[1<<20];
struct Path
{
int pre, id;
}path[1<<20];
void putout(int x)
{
if (!x) return ;
putout(path[x].pre);
printf("%s\n", name[path[x].id]);
}
int main()
{
int T; scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%s%d%d", name[i], &d[i], &c[i]);
memset(dp, INF, sizeof(dp));
memset(cost, 0, sizeof(cost));
dp[0] = 0;
for (int state = 1; state < (1<<n); state++)
{
for (int i = 0; i < n; i++)
{
if (state&(1<<i))
{
int pre = state^(1<<i);
int temp = 0;
if (cost[pre]+c[i]-d[i] > 0)
temp = cost[pre]+c[i]-d[i];
if (dp[state] >= dp[pre]+temp)
{
dp[state] = dp[pre]+temp;
cost[state] = cost[pre]+c[i];
path[state].pre = pre;
path[state].id = i;
}
}
}
}
printf("%d\n", dp[(1<<n)-1]);
putout((1<<n)-1);
}
return 0;
}
/*
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
*/