题意
有n个任务,每个任务有一个截止时间,超过截止时间一天,要扣一个分。
求如何安排任务,使得扣的分数最少。
解题
n个任务,共有n!种执行方式。因为n不超过15,所以用状态压缩的方式来表示状态(即二进制)。
设dp[s]表示以状态s作为终止状态的安排方式的所扣最少分数。
状态转移方程:dp[s]=min{dp[s],dp[last]+执行x任务所需时间},状态last与s差了一个任务x。
因为要求在所扣分数相同的情况下,输出字典序最小的方案。所以,枚举状态last(通过枚举任务x来实现)需要排序枚举x任务。
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
const int N = 16;
struct Node
{
char str[109];
int want, need;
}node[N];
struct DP
{
int now, sum, next, pos;
}dp[1<<N];
void put_ans(int x)
{
if(dp[x].next != -1)
{
put_ans(dp[x].next);
printf("%s\n", node[dp[x].pos].str);
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
for(int i=0; i<n; i++)
scanf("%s%d%d", node[i].str, &node[i].want, &node[i].need);
dp[0].now = dp[0].sum = 0;
dp[0].next = dp[0].pos = -1;
int m = (1<<n)-1;
for(int i=1; i<=m; i++)
{
dp[i].sum = 0x3f3f3f3f;
for(int j=0; j<n; j++)
{
if((1<<j) & i)
{
int k = i - (1<<j);
int v = dp[k].now + node[j].need - node[j].want;
v = max(v, 0);
if(dp[i].sum >= dp[k].sum+v)
{
dp[i].sum = dp[k].sum + v;
dp[i].now = dp[k].now + node[j].need;
dp[i].next = k;
dp[i].pos = j;
}
}
}
}
printf("%d\n", dp[m].sum);
put_ans(m);
}
return 0;
}