题意:每个作业有最后期限和写这个作业要花费的时间。每超过最后期限一个单位时间就会减掉一分。求最少减掉多少分。
思路:状态压缩。最多15个科目。最多就15位就够了。
当这一位是1的时候就表示这个作业已经完成了。全1代表全部完成。动态规划方程:
dp(sta)=min{ dp(from) + (timeused(sta) - deadline(i) ) }
其中dp(sta)代表了在i状态的时候,最少减去的分数。timeused(sta)代表了状态i的时候,需要花费的时间。deadline(i)代表了第i门作业的最后期限。from是能到达sta的状态。from应该是跟sta只有第i位不一样。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[1<<16];
char subject[19][105];
int pre[1<<16];
int dl[19];
int cost[19];
int ans[19];
int n;
inline int timeused(int sta){
int ret=0;
int tot=0;
while(sta>0){
if(sta&1){
ret+=cost[tot];
}
tot++;
sta>>=1;
}
return ret;
}
void getans(int sta){
int tot=0;
while(sta>=0){
if(sta&1){
printf("%s\n",subject[tot]);
return;
}
tot++;
sta>>=1;
}
return;
}
int dfs(int sta)
{
if(dp[sta]>=0)return dp[sta];
for(int bit=n-1;bit>=0;--bit){//从后往前保证字典序
int sub=1<<bit;
if(sta&sub){
int from=sta^sub;
int tu=timeused(from);
int reduse=tu+cost[bit]-dl[bit];
if(reduse<0)reduse =0;
reduse+=dfs(from);
if(dp[sta]>reduse||dp[sta]==-1){
dp[sta]=reduse;
pre[sta]=from;
}
}
}
return dp[sta];
}
int main()
{
// freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
memset(dp,-1,sizeof(dp));
for(int i=0;i<n;++i){
scanf("%s",subject[i]);
scanf("%d%d",&dl[i],&cost[i]);
}
int last=(1<<n)-1;
dp[0]=0;
dfs(last);
printf("%d\n",dp[(1<<n)-1]);
int cnt=0;
for(int i=last;i>0;){
ans[cnt++]=i^pre[i];
i=pre[i];
}
for(int i=n-1;i>=0;--i){
getans(ans[i]);
}
}
return 0;
}