题意:有n门功课,每一门需要c天完成,若在d天还没有完成,之后每一天直到完成,会被扣分数。
一题dp。
用的状态压缩,因为最多15门功课。所以我们可以从1枚举到1<<15 - 1来代表所有功课的完成情况。比如有三门功课101代表,第一门和第三门完成,而第二门没有完成。
用&判断此功课是否在当前枚举量中存在,若存在,便可寻求当前的最优解和跟新父节点。
每一次状态的改变都是在枚举量变化之后,所以从前开始改变功课当前的状态,还是从后开始,并不影响答案。
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 15 + 5;
const int inf = 100000000;
struct node
{
string name;
int dead;
int cost;
}work[maxn];
struct node1
{
int pre, time, cost, now;
}dp[1<<15 + 5];
void print(int n)
{
if(dp[n].pre) print(dp[n].pre);
cout<<work[dp[n].now].name<<endl;
}
int main()
{
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
cin>>work[i].name>>work[i].dead>>work[i].cost;
}
int tot = 1<<n;
dp[0].time = 0;
dp[0].cost = 0;
for(int i = 1; i < tot; i++)
{
dp[i].pre = 0;
dp[i].cost = inf;
for(int s = n -1; s >= 0; s--)
{
if(i & (1<<s))
{
// printf("%d %d\n", i, s);
int past = i - (1<<s);
int waste = dp[past].time + work[s].cost - work[s].dead;
if(waste < 0)
waste = 0;
//printf("%d %d\n", past, dp[past].cost);
if(dp[i].cost > dp[past].cost + waste)
{
//printf("%d %d\n", i, waste);
dp[i].cost = dp[past].cost + waste;//更新扣分次数
dp[i].time = work[s].cost + dp[past].time;//更新此状态的天数
dp[i].pre = past;
dp[i].now = s;
}
}
}
}
printf("%d\n", dp[tot - 1].cost);
print(tot - 1);
}
return 0;
}