CF #536div2E(dp)
简单入门版:洛谷1280
时间交叉和倒序处理的思路是相同的,相较之下此题更多的条件是:1.每个任务可以在很多个时间点中选一个去做;2.会有捣乱。
解决方法:1.每个时间点选哪个根据规则的话是固定的可预处理,但这样会发现某一段时间都是选的那个最大的,然鹅实际上只能选一次对吧。但其实没问题,很难讲,总之冷却时间点大等于最后时间点这个条件保证了倒序处理时不会重复拿这个任务,倒序和能拿必拿这两点很关键。2.捣乱就比较常规的dp了,第一维时间点,第二维捣乱几次了,然后每层按照捣不捣乱转移。
主代码:
1 const int maxn = 1e5 + 5; 2 int n, m, k; 3 ll dp[maxn][205]; 4 map<int, P> mp; 5 vector<P> st[maxn], ed[maxn]; 6 multiset<P> s; 7 8 int main() { 9 read(n), read(m), read(k); 10 rep(i, 1, k) { 11 int ast, aed, d, money; 12 read(ast), read(aed), read(d), read(money); 13 st[ast].push_back(P(-money, -d)); 14 ed[aed + 1].push_back(P(-money, -d)); 15 } 16 rep(i, 1, n) {//预处理每个时间节点应该选择哪个任务 17 for (auto j : st[i]) s.insert(j); 18 for (auto j : ed[i]) s.erase(s.find(j)); 19 if (s.size()) { 20 auto tmp = s.begin(); 21 mp[i] = P(-tmp->first, -tmp->second); 22 } 23 } 24 25 irep(x, n, 1) { 26 //不捣乱 27 if (mp.count(x)) { 28 auto tmp = mp[x]; 29 rep(i, 0, m) 30 dp[x][i] = dp[tmp.second + 1][i] + tmp.first; 31 } else 32 rep(i, 0, m) 33 dp[x][i] = dp[x + 1][i]; 34 //捣乱 35 rep(i, 0, m) { 36 if (i) dp[x][i] = min(dp[x][i], dp[x + 1][i - 1]); 37 } 38 } 39 writeln(dp[1][m]);//时间点1、已经捣乱m次 40 return 0; 41 }