一、很容易想到如下的模拟过程:
(0)使没有先决条件的工作进入服务窗口
(1)找到所有正在处理的工作中剩余所需时间最少的工作所需的时间T
(2)将所有窗口剩余所需时间减去T,如果该窗口的工作已经完成,则将其当做先决条件的工作所需的先决条件的数量减一,如果这一工作已经没有任何先决条件,则将其送入一个窗口开始进行服务
(3)如果所有窗口都已经没有在进行服务,则过程停止
根据以上步骤,很容易写出模拟代码:
#include <cstdio>
#include <vector>
#include <list>
using namespace std;
#define MAX 10001
struct Job{
int timeNeeded;
int numberOfPrerequisites;
vector<int> nextJobs;
} arr[MAX];
int N;
int getLeastTimeNeeded(const list<int>& jobList)
{
list<int>::const_iterator iter = jobList.begin(), eter = jobList.end();
int t = arr[*iter++].timeNeeded, i;
for(; iter != eter; ++iter){
i = *iter;
if(t > arr[i].timeNeeded) t = arr[i].timeNeeded;
}
return t;
}
void insertSubsequentJobs(list<int>& jobList, int doneID)
{
if(arr[doneID].nextJobs.empty()) return;
for(int i = 0, n = arr[doneID].nextJobs.size(); i < n; ++i){
int j = arr[doneID].nextJobs[i];
if(--arr[j].numberOfPrerequisites == 0) jobList.push_front(j);
}
}
int main()
{
//step 1: input job count
scanf("%d", &N);
//step 2: input job graph
int i, j, k;
for(i = 1; i <= N; ++i){
scanf("%d %d", &arr[i].timeNeeded, &arr[i].numberOfPrerequisites);
for(j = 0; j < arr[i].numberOfPrerequisites; ++j){
scanf("%d", &k);
arr[k].nextJobs.push_back(i);
}
}
//step 3: initialize job line
list<int> jobList;
list<int>::iterator iter, eter;
for(i = 1; i <= N; ++i){
if(arr[i].numberOfPrerequisites == 0) jobList.push_front(i);
}
//step 4: schedule jobs
int totalTime = 0, time;
while(!jobList.empty()){
//find out the job which need least time now
totalTime += time = getLeastTimeNeeded(jobList);
//minus each job with time
for(iter = jobList.begin(), eter = jobList.end(); iter != eter; ){
i = *iter;
arr[i].timeNeeded -= time;
if(arr[i].timeNeeded == 0){
iter = jobList.erase(iter);
insertSubsequentJobs(jobList, i);
}
else ++iter;
}
}
//step 5: print result
printf("%d", totalTime);
return 0;
}
二、实际上此题还可以看做树型DP的问题,把每一个工作看成一个节点,将先决条件看成其子节点,则dp[parent] = max{dp[children] + costOfParent},并且题中"
Farmer John's list of chores is nicely ordered, and chore K (K > 1) can have only chores 1,.K-1 as prerequisites."
说明我们可以边读入边DP而不用保存节点信息:
#include <cstdio>
#define MAX 10001
int N, dp[MAX] = {0};
int main()
{
scanf("%d", &N);
int i, j, k, n, t, ans = 0;
for(i = 1; i <= N; ++i){
scanf("%d %d", &t, &n);
if(n == 0) dp[i] = t;
else{
for(j = 0; j < n; ++j){
scanf("%d", &k);
if(dp[k] + t > dp[i]) dp[i] = dp[k] + t;
}
}
if(dp[i] > ans) ans = dp[i];
}
printf("%d", ans);
return 0;
}
从时间上来看,用了DP之后,速度并没有得到质的改变,目测是因为数据量太大,大部分时间消耗在了scanf上:
当大部分时间用在IO上时,改进算法的作用微乎其微