状压DP
题意: 有n门作业,每门作业都有最晚完成时间以及完成该作业需要的天数, 过了最晚完成时间后一天减一分,问最后最少减多
少分就可以完成所有作业以及输出做作业的顺序
思路:因为最多有15门作业,所以考虑状态压缩, 做n门作业时可以由做n-1门作业时再做一门转化而来;
递推方程:dp[i|(1 << j)] = min(dp[i] + ju, dp[i|(1 << j)]); i为状态的遍历, 做一门作业时把该位设为1, 等于不做这一门时的分数加
上再做这一门时应该加上的分数, ju = 做i门需要的时间 + 第j门时间 - 第j门完成时间, 小于0时为0, 不会增加分数;
更新时用path记下更新的路径, 最后通过递归倒序寻找做作业的顺序,输出即可;
代码如下:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct P
{
char s[105];
int d, u;
}t[20];
int path[1 << 15];
int dp[1 << 15];
int n;
int T;
void print(int k)
{
for(int i = 0; i < n; i++)
{
if((k&(1 << i)) != 0 && path[k] == k - (1<<i))
{
//printf("-------------------\n");
print(path[k]);
printf("%s\n", t[i].s);
}
}
}
int main()
{
cin >> T;
int time;
int ju;
while(T--)
{
memset(dp, INF, sizeof(dp));
memset(path, -1, sizeof(path));
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%s%d%d", t[i].s, &t[i].d, &t[i].u);
dp[0] = 0;
for(int i = 0; i < (1 << n); i++)
{
time = 0;
for(int j = 0; j < n; j++)
{
if(i & (1<<j))
{
time += t[j].u;
}
}
for(int j = 0; j < n; j++)
{
if((i & (1 << j)) == 0)
{
if(time + t[j].u < t[j].d)
ju = 0;
else
ju = time + t[j].u - t[j].d;
if(dp[i] + ju < dp[i|(1<<j)])
{
dp[i|(1<<j)] = dp[i] + ju;
path[i|(1<<j)] = i;
}
//dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + ju);
}
}
}
//printf("%d\n", dp[1]);
printf("%d\n", dp[(1 << n) - 1]);
print((1 << n) - 1);
}
return 0;
}