Description
Input
Output
Sample Input
3 3 15 0 25 50 0 90 45 15 70 3 15 5 20 15 25 40 15 45 60 5 3 3 6 3 6 10 3 14 19 6 7 16 4 4 11
Sample Output
50 45 15
思路:
1. 搜索
2. DP. dp[i] 表示从时间 i 到 endtime 之间工作的最小值
dp[i] = min(dp[i+t[j]]+t[j]), t[j] 表示第 j 个任务的执行时间
3. 由(2) 的状态转移方程看, 需要计算在时刻 t 都有哪些任务可做, 时间复杂度 o(m*n) m 是 endtime, n 是 工作数, 且根据题意, endtime < 250, n < 100
总结:
1. 按照思路 (2) 的状态转移方程来做的话, 需要防止一个任务被重复计算两次. 一个直接的应对方法是再加一维, 那一维可通过状态压缩的方法表示那些任务已经被计算过了
2. 这段代码曾忘掉
if(!job[i].size()) { // 没有任务可做 dp[i] = dp[i+1]; continue; }
代码:
WA 到死
#include <iostream> #include <vector> using namespace std; const int MAXN = 1010; const int INF = 0X3F3F3F3F; int t[MAXN], a[MAXN], d[MAXN]; vector<int> job[MAXN]; int n, endTime, startTime; int dp[MAXN]; int cases; void pre_process() { for(int i = 0; i < MAXN; i ++) { job[i].clear(); } for(int i = startTime; i <= endTime; i ++) { for(int j = 1; j <= n; j ++) { if(i >= a[j] && i+t[j] <= d[j]) job[i].push_back(j); } } memset(dp, 0x3f, sizeof(dp)); dp[endTime] = 0; } int mainFunc() { for(int i = endTime-1; i >= startTime; i --) { if(!job[i].size()) { // 没有任务可做 dp[i] = dp[i+1]; continue; } dp[i] = INF; for(int j = 0; j < job[i].size(); j ++) { int curJob = job[i][j]; int ti = t[curJob]; dp[i] = min(dp[i], dp[i+ti]+ti); } } return dp[startTime]; } int main() { freopen("E:\\Copy\\ACM\\poj\\1337\\in.txt", "r", stdin); cin >> cases; while(cases-- >= 1) { endTime = 0; startTime = 1000; cin >> n; for(int i = 1; i <= n; i ++) { scanf("%d%d%d", &t[i], &a[i], &d[i]); endTime = max(endTime, d[i]); startTime = min(startTime, a[i]); } pre_process(); // mainFunc cout << mainFunc() << endl; } return 0; }
update 2014年3月15日17:05:31
进行预处理之后, 这道题就变成了常见的朴素 01 背包, 比如 Leetcode wordbreak 什么的