题意是一个刚从赛场回来的ACMer,要补作业,每个作业都有完成的时间和截止期限,超过截止期限要受到惩罚。问
怎样安排写作业的次序使得受到的惩罚最小,如果有多个答案,输出字典序最小的。
思路
状态压缩,dp
因为完成作业的状态有很多种,可以完成了这个,再去完成另一个,很容易想到用一个集合表示,即状态压缩。最后记忆
化搜索即可。
#include <bits/stdc++.h>
using namespace std;
#define min(x, y) ((x) > (y) ? (y) : (x))
#define max(x, y) ((x) < (y) ? (y) : (x))
int n;
const int maxn = (1 << 16);
const int maxd = 17;
string ss[maxd];
int c[maxd], da[maxd];
int d[maxn];
bool vis[maxn];
const int inf = 0x3f3f3f3f;
int dp(int S, int day){
int & val = d[S];
if(vis[S]) return val;
vis[S] = true;
if(S == ((1 << n) - 1)) return val = 0;
val = inf;
for(int i = 0; i < n; ++i) if(!((1 << i) & S)){
int s = (1 << i) | S;
int cost = max(0, day - da[i] + c[i]);
val = min(val, dp(s, day + c[i]) + cost);
}
return val;
}
void print(int S, int day){
int & val = d[S];
if(S == ((1 << n) - 1)) return;
for(int i = 0; i < n; ++i) if(!((1 << i) & S)){
int s = (1 << i) | S;
int cost = max(0, day - da[i] + c[i]);
if(d[s] + cost == val){
printf("%s\n", ss[i].c_str());
print(s, day + c[i]);
return;
}
}
}
int main()
{
int T; scanf("%d", &T);
while(T --){
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
cin >> ss[i];
scanf("%d%d", &da[i], &c[i]);
}
memset(vis, false, sizeof(vis));
printf("%d\n", dp(0, 0));
print(0, 0);
}
return 0;
}