您的位置:首页 > 其它

HDU 1074 Doing Homework 状态压缩dp

2016-10-26 10:37 288 查看
http://acm.hdu.edu.cn/showproblem.php?pid=1074

题目是给定n个科目(n <= 15)然后每个科目有最迟完成时间和需要多少天去完成。

现在要你安排一个顺序去做这些科目,使得扣分最小,要求输出字典序最小的解。

考虑用dp[i]表示完成了i的二进制那些科目时,所需的最小扣分。比如i(1001)就是完成了第1科和第四科。

那么枚举这样的每一个状态,再枚举每一个科目,如果当前这个状态还没有完成这个科目的话,就是(i & (1 << j)) == 0的话。

就可以进行转移了。可以用dp记录很多信息,包括当前状态的前一个状态(用于记录路径)和当前状态是选了哪一个科目(每一个新状态完成一个科目)和完成了当前状态的那些科目后,所需的最小扣分。和完成了当前状态的那些科目后,用了多少时间(这个用来记录后面枚举新科目的时候,是否会扣分)

其实你想维护什么,dp那里都可以记录。关键是转移的时候要按照扣分的多少来转移就是了。

如果从10001枚举到10011所需的扣分比从10010枚举到10011所需的最小扣分要小,

那么就要更新了,但是反过来,如果是相等呢?

相等的话,是不用变的,

因为答案需要的是最小字典序解

从10001枚举到10011

就是先做1和5,再做4

从10010枚举到10011

就是先做1和4,再做5

他们的cost相同,但是明显下面的字典序比较小(因为题目已经排好序)

所以,是不用变了

最后,dp数组要开到 1 << 15

一开始开了15,一直TLE

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
struct nodeInformation {
char name[100 + 20];
int dead;
int need;
}a[15 + 20];
struct nodeDp {
int pre;
int cur; //当前这个是什么

int cost; //记录扣多少分
int tim; //什么时候完成了这个状态
}dp[1 << 16];
void show(int val) {
if (val == 0) return;
show(dp[val].pre);
printf("%s\n", a[dp[val].cur].name);
}
void work() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%s%d%d", a[i].name, &a[i].dead, &a[i].need);
}
int end = 1 << n;
memset(dp, 0x3f, sizeof dp);
dp[0].cost = 0;
dp[0].pre = -inf;
dp[0].tim = 0; //一科都没完成的状态
dp[0].cur = -inf;
for (int i = 0; i < end; ++i) { //枚举每一个状态
for (int j = 0; j < n; ++j) {
if ((i & (1 << j)) == 0) { //如果没完成到这个科目,才能转移
int newState = i | (1 << j);
int dayTofinish = dp[i].tim + a[j].need;
int cost = dayTofinish - a[j].dead;
if (cost < 0) cost = 0; //就是提前完成了,不用扣费
cost += dp[i].cost;

if (cost < dp[newState].cost) { //现在这样花费比以前的小
dp[newState].cost = cost;
dp[newState].cur = j;
dp[newState].pre = i; //i这个状态
dp[newState].tim = dayTofinish;
}
}
}
}
printf("%d\n", dp[end - 1].cost);
show(end - 1);
}

int main() {
#ifdef local
freopen("data.txt","r",stdin);
#endif
int t;
scanf("%d", &t);
while (t--) work();
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: