HDU ACM 1074 Doing Homework->状态压缩DP(集合上的DP)

分析:n种家庭作业,全部做完有n!种顺序,但是n!太大了,而且对于作业1,2,3和1,3,2和2,1,3和3,2,1和3,1,2的顺序来说完成它们消耗的天数是一样的,只是顺序不同从而扣分不同,所以可将完成相同的作业的所有状态压缩成一种状态并记录扣的最少分即可 。

状态压缩DP采用二制进的思想,1, 0代表完成否,如:3的二进制为11, 代表完成了一,二科目的状态, 101代表完成了一、三科目的状态。这样,可从0->(1 << N)来获取所有状态,,进行适当的状态转移,对于该題D[s]代表集合s的状态, 要得到D[s], 可从0 - N 分别检查是否在集合s内s & (1 << i) > 0则表示i在集合s上, 如果i在s集合内, 刚D[s]可从D[s-{i}]来获得, s-{i},可以用s - (1<<i)来计算,这样表示在已完成了s-{i}的基础上再完成i后的装态,遍历i,取最优解。

用dp[i]记录完成作业状态为i时的信息,递推如下:

1.状态a能做第i号作业的条件是a中作业i尚未完成,即a&i=0。

2.若有两个状态dp[a],dp[b]都能到达dp[i],那么选择能使到达i扣分小的那一条路径,若分数相同,转入3

3.这两种状态扣的分数相同,那么选择字典序小的,由于作业按字典序输入,故即dp[i].pre = min(a,b);

关键要理解这张图,每一个状态表示完成了哪些任务,如二进制100表示完成了第三项作业,111表示三项作业都完成。状态转移为将能够到达的状态的某个位上的1去掉剩下的二进制序列能够转移到原状态,例如:110可能来自两个状态100 010。状态压缩dp就是将状态用二进制表示,接下来就是枚举,该题要注意的一点,输出的顺序,由于需要按照字母序输出,所以按照逆序遍历的意义在于,假设前面的1已经被添加过了,则先去添加后面的1。

#include<iostream>
#include<limits.h>
#include<string>
using namespace std;

struct homework
{
	string name;     //家庭作业名字
	int dl;     //截止时间
	int ct;     //需要的时间
} hw[16];

struct DP
{
	int pre;    //前一个状态
	int curtime;     //当前时间
	int score;       //分数
	string name;
} dp[65536];         //2^15=32768

void put(int end)    //递归输出结果
{
	if(dp[end].pre==0)
	{
		cout<<dp[end].name<<endl;
		return ;
	}
	put(dp[end].pre);
	cout<<dp[end].name<<endl;
}

int solve(int n)
{
	int i,j,end,tmp;

	dp[0].score=0;     //初始状态0
	dp[0].curtime=0;
	end=1<<n;
	for(i=1;i<end;i++)
	{
		dp[i].score=INT_MAX;    //初始为最大值
		for(j=n-1;j>=0;j--)
			if(i&(1<<j))    //若放入第j个作业
			{
				tmp=0;
				if(dp[i^(1<<j)].curtime+hw[j].ct-hw[j].dl>0)  //第j个作业是否会扣分
					tmp=dp[i^(1<<j)].curtime+hw[j].ct-hw[j].dl;
				if(dp[i].score>dp[i^(1<<j)].score+tmp)
				{
					dp[i].score=dp[i^(1<<j)].score+tmp;
					dp[i].pre=i^(1<<j);           //i^(1<<j)表示i的前一个状态,即i中出去位j
					dp[i].curtime=dp[i^(1<<j)].curtime+hw[j].ct;
					dp[i].name=hw[j].name;
				}
			}
	}
	return dp[end-1].score;
}

int main()
{
	int i,T,N;

	cin>>T;
	while(T--)
	{
		cin>>N;
		for(i=0;i<N;i++)
			cin>>hw[i].name>>hw[i].dl>>hw[i].ct;
		cout<<solve(N)<<endl;         //最小被扣分数
		put((1<<N)-1);
	}
    return 0;
}
时间: 2024-11-13 05:00:30

HDU ACM 1074 Doing Homework->状态压缩DP(集合上的DP)的相关文章

HDU 1074 Doing Homework(状态压缩 + DP)

Problem Description: Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will

HDU 1074 Doing Homework(状态压缩DP)

题意:有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小 思路:二进制表示. 1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstdio> 6 #include<set> 7 #include<map> 8

HDU 4975 A simple Gaussian elimination problem. 网络流+矩阵上的dp

随机输出保平安啊 和hdu4888一个意思,先跑个网络流然后dp判可行. ==n^3的dp过不了,所以把n改成200. ==因为出题人没有把多解的情况放在200*200以外的矩阵. #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int MAX_N = 12

HDU1074 Doing Homework 状态压缩dp

题目大意: 根据完成任务的截止时间,超时一天罚1分,求完成所有任务后的最小罚时 这里n最大为15,可以利用状态压缩来解决问题 1 /* 2 首先要明白的一点是状态1/0分别表示这件事做了还是没做 3 而1/0的位置表示这是哪一件事 4 比如说 5 可以表示为101,那么表示第一个和第三个任务已经完成 5 而dp[5]表示第一个和第三个任务完成所花费的最短时间 6 而状态5(101)是从状态1(001)或者状态4(100)转移过来的 7 也就是说我们总是可以通过一个较小的状态不断递推一个较大状态的

!HDU 1074 Doing Homework--DP--(状态压缩)

题意:n个作业,每个作业有deadline和做完这个作业需要花的时间cost,完成作业每超过一天就减一分,求减去的最小的分数 分析:作业的全排列中取最优解,但是15!太大了会超时,所以用二进制来状态压缩,15个二进制位,第i位的0/1代表第i个作业是否完成. 1.会用状态压缩 2.保存和输出最优解序列方法 慢慢加深理解吧 代码: #include<iostream> #include<cstring> #include<string> #define INF 1<

hdu 4778 Gems Fight!(状态压缩+博弈+记忆化)

Gems Fight! Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others) Total Submission(s): 1383    Accepted Submission(s): 587 Problem Description Alice and Bob are playing "Gems Fight!": There are Gems of G differe

Wireless Password - HDU 2825(ac自动机+状态压缩)

题目大意:有个人想破解他邻居的密码,他邻居告诉了一些关于这个密码的信息,并且给他一个单词集合,他用这些信息判断一下最少有多少种密码. 1->, 所有的密码都是有小写字母组成. 2->,密码的长度是 n (1<= n <=25). 3->,密码至少包含 k 种字符集里面的单词. 比如,给集合{"she", "he"},单词长度是3,最少包含两个单词的密码,很明显只能是“she”(题目表述的不清楚).   分析:因为要统计记录到达每个点时候

ACM/ICPC 之 BFS+状态压缩(POJ1324(ZOJ1361))

求一条蛇到(1,1)的最短路长,题目不简单,状态较多,需要考虑状态压缩,ZOJ的数据似乎比POj弱一些 POJ1324(ZOJ1361)-Holedox Moving 题意:一条已知初始状态的蛇,求其到(1,1)的最短路长 题解:开始做的时候用BFS暴力做了一次,结果RE了,后来看了其他的题解和discuss才转向状态压缩.也看到有人用A*做出来了. 现在简要介绍一下状态压缩的思路: 由于蛇身最长只有8,可以利用两条相邻蛇身坐标确定其相对方向(四个方向),两位二进制可以表示 这样 一个蛇头坐标+

hdu 3681 Prison Break (状态压缩+bfs+最短路)

Prison Break Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3214    Accepted Submission(s): 829 Problem Description Rompire is a robot kingdom and a lot of robots live there peacefully. But on