状态压缩 之 hdu 1074 Doing Homework

//  [9/11/2014 Sjm]
/*
hdu 1074 (状态压缩)
dp[i]: 表示在 i 状态时的最优解 
(i状态: i数值用二进制表示, 若二进制数第 n 位为1,代表第 n 个课程已被计算)
思路: 详见代码注释(举几个简单例子模拟一下,可以明白的更清楚一些)
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int MAX = 1 << 16, MAX_N = 20, INF = 0x3f3f3f3f;
int N;
bool Vis[MAX];

struct myNode {
	int nowTime; // 当前时间
	int pre; // 前一个状态
	int minReduce; // 被扣掉的分数
} dp[MAX];

struct mySubject {
	string name; // 课程名
	int deadline; // 最后期限
	int cost; // 所需要花费的时间
} subject[MAX_N];

int getPos(int x) {
	int pos = 0;
	while (x) {
		++pos;
		x >>= 1;
	}
	return (pos - 1);
}

void Output(int State) {
	if (0 == State) return;
	int nowSub = (State^dp[State].pre); // 为了获取当前课程是第几个 ==> getPos(nowSub)
	Output(dp[State].pre);
	cout << subject[getPos(nowSub)].name << endl;
}

void Solve() {
	memset(Vis, false, sizeof(Vis));
	for (int i = 0; i < N; ++i) dp[i].minReduce = INF;
	dp[0].nowTime = 0;
	dp[0].pre = -1;
	dp[0].minReduce = 0;
	Vis[0] = true;
	int finalState = (1<<N) - 1;
	for (int state = 0; state < finalState; ++state) {  // 遍历所有状态
		for (int subPos = 0; subPos < N; ++subPos) { // 在当前状态下加入subPos课程
			int sub = (1 << subPos);
			if (0 == (sub&state)) { // 判断当前subPos课程是否已经计算过,若没有则进行计算,否则需寻找其他课程
				int nowState = state | sub; // 计算过subPos课程后,所到达的新的状态
				int finish_nowState_Time = dp[state].nowTime + subject[subPos].cost; 
				// 完成subPos课程后,整个状态所到达的时间
				int nowStateReduce = finish_nowState_Time - subject[subPos].deadline; 
				if (nowStateReduce < 0) nowStateReduce = 0; 
				// 仅仅因为subPos课程所需要被扣掉的分数
				nowStateReduce = dp[state].minReduce + nowStateReduce; 
				// 完成subPos课程后,整个状态需要被扣掉的分数
				if (Vis[nowState]) { 
					// 若新的状态之前到达过,则与完成subPos课程到达此状态时,所被扣掉的分数进行比较
					// 若之前到达该状态时,所被扣掉的分数 > 完成subPos课程到达此状态时,所被扣掉的分数
					// 则对该状态进行更新
					if (dp[nowState].minReduce > nowStateReduce) {
						dp[nowState].minReduce = nowStateReduce;
						dp[nowState].pre = state;
						dp[nowState].nowTime = finish_nowState_Time;
					}
				}
				else {
					// 若新的状态之前没有到达过,直接赋值更新
					Vis[nowState] = true;
					dp[nowState].minReduce = nowStateReduce;
					dp[nowState].pre = state;
					dp[nowState].nowTime = finish_nowState_Time;
				}
			}
		}
	}
	printf("%d\n", dp[finalState].minReduce); 
	Output(finalState); // 递归输出所选择课程的顺序
}

int main() {
	//freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
	int Case;
	scanf("%d", &Case);
	while (Case--) {
		scanf("%d", &N);
		for (int i = 0; i < N; ++i) {
			cin >> subject[i].name;
			scanf("%d %d", &subject[i].deadline, &subject[i].cost);
		}
		Solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值