题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074
有n(n<=15)门课需要做作业,每门课所需时间是used_time以及每门课作业上交的最后期限是deadline,晚交一天扣一分,现在来安排最好的写作业计划,让最终的扣除分数最少;
由于n的取值范围不大所以我们可以枚举除所有的状态进行判断是否是最优的即可,状态数为2^n-1;
我们可以用状态压缩来表示出各种状态;二进制中的第i为为1代表第i门课已经完成了。
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <vector> #include <algorithm> #include <string> using namespace std; #define N 1050 #define MOD 1000000007 #define met(a, b) memset(a, b, sizeof(a)) #define INF 0x3f3f3f3f typedef long long LL; int n, pre[N]; struct course { char name[220]; int deadline, use_time; } Course[20]; struct Status { int scores;///当前状态能扣除的最少分数; int times;///当前状态所需的总时间; } dp[1<<16]; void solve(Status& A, Status B, course C)///更新dp; { int Times = B.times + C.use_time; int Score = B.scores + max(Times - C.deadline, 0); if( A.scores > Score || ( A.scores == Score && A.times > Times )) { A.scores = Score; A.times = Times; } } Status dfs(int sta) { if( dp[sta].scores != -1 )///记忆化搜索就是防止一个状态被重复搜索,所以要更新dp的值; return dp[sta]; dp[sta].scores = INF; for(int i=0; i<n; i++) { if( sta & (1<<i) )///表示当前状态第i门课已经完成了。所以我们要找达到这种状态的其他状态; solve(dp[sta], dfs(sta-(1<<i)), Course[i]);///sta的状态是由sta-(1<<i)加上第i门课得到的; } return dp[sta]; } bool Judge(Status A, Status B, course C) { int Times = B.times + C.use_time; int Score = B.scores + max(Times - C.deadline, 0); return A.scores == Score && A.times == Times; } void Puts_Path(int sta) { if(sta == 0) return ; for(int i=n-1; i>=0; i--) { if( sta&(1<<i) && Judge(dp[sta], dp[sta-(1<<i)], Course[i]) )///判断当前的状态dp[sta]值是不是由dp[sta-(1<<i)]和Course得到; { Puts_Path( sta-(1<<i) ); printf("%s\n", Course[i].name);///路径是倒着找的的所以应在回溯的过程输出 break; } } } int main() { int T; scanf("%d", &T); while(T--) { met(dp, -1); met(Course, 0); scanf("%d", &n); for(int i=0; i<n; i++) scanf("%s %d %d", Course[i].name, &Course[i].deadline, &Course[i].use_time); dp[0].scores = dp[0].times = 0; Status ans = dfs( (1<<n) - 1 );///从最后一个状态开始找; printf("%d\n", ans.scores); Puts_Path( (1<<n) - 1 );///输出路径; } return 0; }
记忆化搜索
时间: 2024-10-12 16:08:41