题意:有N(N<15)个作业要做,如果一门课的作业没做完,超过一天,就要罚一分。问:最少罚多少分?并输出做作业的先后顺序,如果有相同罚分的,输出字典序最小的。
思路:状态压缩。用二进制表示做作业的状态:101表示第一科作业和第3科作业都做了的最少罚分,这个可以由100,001两个状态转移过来。
http://acm.hdu.edu.cn/showproblem.php?pid=1074
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define rep(i,a,b) for(LL i = (a) ; i <= (b) ; i ++)
#define rrep(i,a,b) for(LL i = (b) ; i >= (a) ; i --)
#define repE(p,u) for(Edge * p = G[u].first ; p ; p = p -> next)
#define cls(a,x) memset(a,x,sizeof(a))
#define eps 1e-8
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
LL T,n,m,k;
struct Node {
char name[105];
LL dt,ot;
}t[16];
struct NODE{
LL route[16] ;
LL use ;
LL cost ;
}dp[(1<<15)+5];
LL root[(1<<15)+5];
void init() {
LL tmp;
rep(i,1,1<<15) {
tmp = i;
while(tmp) {
if(tmp&1) root[i] ++;
tmp >>= 1;
}
}
}
void input() {
scanf("%d",&n);
rep(i,1,n) {
scanf("%s %d %d",t[i].name,&t[i].dt,&t[i].ot);
}
}
void solve() {
rep(i,1,1<<15) dp[i].cost = (LL)1e50;
rep(i,1,n) {
dp[1<<(i-1)].use = t[i].ot;
dp[1<<(i-1)].cost = t[i].ot > t[i].dt ? t[i].ot - t[i].dt : 0;
dp[1<<(i-1)].route[1] = i;
}
LL newcost ;
rep(i,2,n) {
rep(j,1,(1<<n)-1) {
if(root[j] == i) {
rrep(ia,0,n-1) {
if((1<<ia)&j) {
newcost = (t[ia+1].ot + dp[j-(1<<ia)].use) > t[ia+1].dt ? (t[ia+1].ot + dp[j-(1<<ia)].use - t[ia+1].dt) : 0;
newcost += dp[j-(1<<ia)].cost ;
if(newcost < dp[j].cost) {
dp[j].cost = newcost;
rep(ib,1,i-1) dp[j].route[ib] = dp[j-(1<<ia)].route[ib];
dp[j].route[i] = ia + 1;
dp[j].use = t[ia+1].ot + dp[j-(1<<ia)].use;
}
}
}
}
}
}
printf("%d\n",dp[(1<<n)-1].cost);
rep(i,1,n) {
printf("%s\n",t[dp[(1<<n)-1].route[i]].name);
}
}
int main(void) {
init();
scanf("%d",&T); while(T--) {
input();
solve();
}
return 0;
}