题意:有n门课,每门课都要交作业,作业有时间花费和最晚提交日期,如果超过日期交作业,每超过一天就要扣一分,问最少扣多少分并且给出最少扣分的方案。
题解:
因为最多只有15门课,所以可以压缩成一个数,二进制位上每一位代表一门课是否完成。然后开始DP。
枚举从1到1<< n的每一个状态i,然后枚举每一门课j(倒叙枚举,因为输入是按照字典序递增输入的),检查状态i是否完成了第j门课(可以通过与运算完成),如果完成了第j门课,计算不完成第j门课时所扣的分加上完成第j门课所扣的分是否比当前状态i所扣的分少,如果少于则进行更新分数和时间,并记下j作为路径输出。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = (1 << 15) + 10;
const int inf = 99999999 ;
int dp[maxn],time[maxn],n,t,dead[20],cost[20],pre[maxn];
char s[20][105];
void solve()
{
int maxm = 1 << n;
for (int i = 1; i < maxm; i++)
{
dp[i] = inf;
for (int j = n - 1; j >= 0; j--)
{
int temp = 1 << j;
if(!(i & temp))continue;
int score = time[i - temp] + cost[j] - dead[j];
if(score < 0)score = 0;
if(dp[i - temp] + score < dp[i])
{
dp[i] = dp[i - temp] + score;
time[i] = time[i - temp] + cost[j];
pre[i] = j;
}
}
}
}
void print(int ans)
{
if(!ans)return ;
print(ans - (1<<pre[ans]));
printf("%s\n",s[pre[ans]]);
}
int main()
{
scanf("%d",&t);
while (t--)
{
memset(s,0,sizeof s);
memset(dead,0,sizeof dead);
memset(cost,0,sizeof cost);
memset(time,0,sizeof dp);
memset(pre,0,sizeof pre);
scanf("%d",&n);
for (int i = 0; i < n; i++)
scanf("%s %d %d", &s[i],&dead[i],&cost[i]);
solve();
int ans = (1 << n) - 1;
printf("%d\n",dp[ans]);
print(ans);
}
}