您的位置:首页 > 其它

HDU1074 Doing Homework 状态压缩DP

2013-11-21 19:11 281 查看

题目大意

题目链接
题目大意就是给N种作业,每种作业都有一个花费时间和截止日期,如果超出截止日期,超出多少天扣多少分,问的是用什么顺序做作业能使得扣得分最少

解题思路

N等于15,全排列枚举非常不合理。
背包,无法求出来顺序。
既然N最大是15,数据规模非常小,可以用状态压缩DP来解。
dp[i]表示i做i集合里面的作业所能做到的最少扣分,然后设置一个j,表示当前面临的是第j个作业,当然j必须存在于i集合中,那么需要做的决策就是做不做j,那么状态转移方程就非常直观dp[i] = min{dp[i - j] + score[j]},当然score[j]表示的是当前的时间做j扣的分数。

实际上这个算法就是相当于在枚举所有的排列,只不过我们将一些排列的共性部分给记录下来,少进行了很多搜索而已。
第一道状压……很大程度上的参考,虽然当时往状压上面想了想,但是没想到这种思想。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
const int maxn = 20;
const int INF = ~0u >> 1;
struct homework{
string name;
int d, c;
void get(){cin >> name; scanf("%d%d", &d, &c);}
}h[maxn];
struct point{
int now, pre;
int now_time;
int score;
int key;
}dp[33000];
int n;
void solve(){
scanf("%d", &n);
for (int i = 0; i < n; i++) h[i].get();
dp[0].now_time = 0; dp[0].score = 0; dp[0].now = 0;
for (int i = 1; i < (1 << n); i++){
dp[i].score = INF;
for (int j = n - 1; j >= 0; j--)
if (i & (1 << j)){
int pre_id = i - (1 << j), tmp = 0;
if (dp[pre_id].now_time + h[j].c > h[j].d)
tmp = dp[pre_id].now_time + h[j].c - h[j].d;
else tmp = 0;
if (dp[pre_id].score + tmp < dp[i].score){
dp[i].score = dp[pre_id].score + tmp;
dp[i].now = i; dp[i].pre = pre_id;
dp[i].now_time = dp[pre_id].now_time + h[j].c;
dp[i].key = j;
}
}
}
stack<string> stk;
cout << dp[(1 << n) - 1].score << endl;
int t = (1 << n) - 1;
while(dp[t].now != 0){
stk.push(h[dp[t].key].name);
t = dp[t].pre;
}
while(!stk.empty()){
cout << stk.top() << endl;
stk.pop();
}
}
int main(){
int cs; scanf("%d", &cs);
while(cs--) solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: