//起初想到贪心没思路 看了题解 状态压缩DP 最多15个状态
// [1, 1<<n) 枚举各个状态推出的每种可能即可
//注意题意说如果答案相同输出字典序最优的答案
//只需要在枚举的时候处理下就好 这里理解了好一会儿
#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "algorithm"
#include <queue>
#include <stack>
#define N (1 << 15) + 5
#define INF 1<<30
using namespace std;
int dp[N], Time[N], pre[N], n, ans; //dp[N]存放到达各个状态最少的扣分数,Time[N]存放到达各个状态扣分最少所需的时间,pre[N]各个状态的前驱
stack<int> sta;
struct node{
int deadline, cost;
char name[105];
} subjuct[20]; //各个科目的作业
int main()
{
int t;
//freopen("t", "r", stdin);
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%s%d%d", subjuct[i].name, &subjuct[i].deadline, &subjuct[i].cost);
ans = 1 << n;
for(int i = 1; i < ans; i++){
dp[i] = INF;
for(int j = n; j >= 1; j--){ //这里是重点 为了保证输出的答案满足字典序最优 这里是j从最后一个作业往前枚举
int tmp = 1 << (j-1); //因为一旦j枚举成功 就相当于作业j放在最后完成
if(tmp & i)
{
int last = i - tmp; //第j个作业没做的之前的状态
int cost = max(Time[last] + subjuct[j].cost - subjuct[j].deadline, 0); //计算做了第J个作业增加的扣分数
if(dp[i] > dp[last] + cost) //如果扣分数小于之前的方案就更新
{
dp[i] = dp[last] + cost;
pre[i] = j; //更新前驱
Time[i] = Time[last] + subjuct[j].cost; //更新到达状态i的时间值
}
}
}
}
printf("%d\n", dp[ans - 1]);
int tmp = ans - 1;
while(tmp) //找前驱并入栈
{
sta.push( pre[tmp] );
tmp -= 1 << (pre[tmp] - 1);
}
while(!sta.empty()) //输出结果
{
printf("%s\n", subjuct[sta.top()].name);
sta.pop();
}
}
return 0;
}
HDU 1074(状态压缩dp)
最新推荐文章于 2022-11-24 20:47:30 发布