动态规划训练16 [Doing Homework HDU - 1074 ]

Doing Homework

  HDU - 1074 


这是一道状态压缩DP(从N <= 15就可以看出来)。

我们定义二进制状态S代表的是目前已经安排好的任务

dp[S].val代表的是目前已经安排好的任务的扣分的最小值

dp[S].sumT代表的是目前已经安排好的任务所需要的时间

状态转移的时候我们只考虑下一个任务的选取

设S通过新任务的选取转移到下一个状态tar

tar = S | (1<<i) ;选取第i个任务

那么状态转移一可以写成

if(dp[S].sumT + Ts[i] > ddls[i])//当新任务的选取超过了ddl而被扣分,那么计算扣分
	cost = dp[S].sumT + Ts[i] - ddls[i];

if(dp[tar].val > dp[S].val + cost ){//更新
	dp[tar].val = dp[S].val + cost;
	dp[tar].sumT = dp[S].sumT + Ts[i];
	dp[tar].pre = S;//设置前驱,记录路径
}
完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node{
	int sumT;
	int val;
	int pre;
};
node dp[(1<<15)];
const int INF = 1e9;
const int MAX = 20;
int ddls[MAX];
int Ts[MAX];
char names[MAX][100];
int order[16];
int calc(int x){
	int cnt = 0;
	while(x){
		cnt++;
		x >>= 1;
	}
	return cnt-1;
}
int main(){
	int T;scanf("%d",&T);
	while(T--){
		for(int i = 0;i < (1<<15);i++){
			dp[i].val = INF;
		}
		int n;scanf("%d",&n);
		for(int i = 0;i < n;i++){
			scanf("%s %d%d",names[i],&ddls[i],&Ts[i]);
		}
		dp[0].pre = 0;
		dp[0].sumT = 0;
		dp[0].val = 0;
		for(int S = 0;S < (1<<n);S++){
			for(int i = 0;i < n;i++){
				if((S & (1<<i)) == 0){
					int tar = S|(1<<i);
					int cost = 0;
					if(dp[S].sumT + Ts[i] > ddls[i])
						cost = dp[S].sumT + Ts[i] - ddls[i];
					if(dp[tar].val > dp[S].val + cost ){
						dp[tar].val = dp[S].val + cost;
						dp[tar].sumT = dp[S].sumT + Ts[i];
						dp[tar].pre = S;
					}
				}
			}
		}
		cout<<dp[(1<<n)-1].val<<endl;
		int S = (1<<n)-1;
		int cnt = n;
		while(S){
			int nS = dp[S].pre;
			int t = calc(nS ^ S);
			order[--cnt] = t;
			S = nS;
		}
		for(int i = 0;i < n;i++)
		{
			cout<<names[order[i]]<<endl;
		}
	}
	return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值