Vijos 1404 遭遇战 - 动态规划 - 线段树 - 最短路 - 堆

背景

你知道吗,SQ Class的人都很喜欢打CS。(不知道CS是什么的人不用参加这次比赛)。

描述

今天,他们在打一张叫DUSTII的地图,万恶的恐怖分子要炸掉藏在A区的SQC论坛服务器!我们SQC的人誓死不屈,即将于恐怖分子展开激战,准备让一个人守着A区,这样恐怖分子就不能炸掉服务器了。(一个人就能守住??这人是机械战警还是霹雳游侠?)
但是问题随之出现了,由于DustII中风景秀丽,而且不收门票,所以n名反恐精英们很喜欢在这里散步,喝茶。他们不愿意去单独守在荒无人烟的A区,在指挥官的一再命令下,他们终于妥协了,但是他们每个人都要求能继续旅游,于是给出了自己的空闲时间,而且你强大的情报系统告诉了你恐怖份子计划的进攻时间(从s时刻到e时刻)。

当然,精明的SQC成员不会为你免费服务,他们还要收取一定的佣金(注意,只要你聘用这个队员,不论他的执勤时间多少,都要付所有被要求的佣金)。身为指挥官的你,看看口袋里不多的资金(上头真抠!),需要安排一个计划,雇佣一些队员,让他们在保证在进攻时间里每时每刻都有人员执勤,花费的最少资金。

格式

输入格式

第一行是三个整数n(1≤n≤10000),s和e(1≤s≤e≤90000)。

接下来n行,描述每个反恐队员的信息:空闲的时间si, ei(1≤si≤ei≤90000)和佣金ci(1≤ci≤300000)。

输出格式

一个整数,最少需支付的佣金,如果无解,输出“-1”。

样例1

样例输入1

3 1 5
1 3 3
4 5 2
1 1 1

样例输出1

5

限制

提示

敌人从1时刻到4时刻要来进攻,一共有3名反恐队员。第1名从1时刻到3时刻有空,要3元钱(买糖都不够??)。以此类推。

一共要付5元钱,选用第1名和第2名。

来源

SQ CLASS公开编程竞赛2008——Problem D
Source: WindTalker, liuichou, royZhang


题目大意

  数轴上有一些区间,每个区间有一个费用。要求选择一些区间将$[s, t]$覆盖,问最小的总费用。

Solution#1 Dynamic Programming & Heap Optimization

  dp是显然的。 用$f[i]$表示将$[s, i]$覆盖的最小费用。

  考虑转移。显然通过能够覆盖点$i$的线段进行转移,因此有

$f[i] = \max_{线段j能覆盖点i}\left\{ f[l_{j} - 1] + w_{j}\right\}$

  显然可以用一个堆维护右边的那一坨。

Code

  1 /**
  2  * Vijos
  3  * Problem#1404
  4  * Accepted
  5  * Time: 98ms
  6  * Memory: 3.48m
  7  */ 
  8 #include <bits/stdc++.h>
  9 #ifndef WIN32
 10 #define Auto "%lld"
 11 #else
 12 #define Auto "%I64d"
 13 #endif
 14 using namespace std;
 15 typedef bool boolean;
 16 
 17 #define ll long long
 18 
 19 typedef class Segment {
 20     public:
 21         int l, r;
 22         int w;
 23         
 24         Segment(int l = 0, int r = 0, int w = 0):l(l), r(r), w(w) {        }
 25         
 26         boolean operator < (Segment b) const {
 27             return l < b.l;
 28         }
 29 }Segment;
 30 
 31 typedef class Heap {
 32     public:
 33         priority_queue<ll, vector<int>, greater<int> > que;
 34         priority_queue<ll, vector<int>, greater<int> > del;
 35         
 36         Heap() {    }
 37 
 38         ll top() {
 39             while (!del.empty() && que.top() == del.top())
 40                 que.pop(), del.pop();
 41             return que.top();
 42         }
 43         
 44         void push(ll x) {
 45             que.push(x);
 46         }
 47         
 48         void remove(ll x) {
 49             del.push(x);
 50         }
 51         
 52         boolean empty() {
 53             return (que.size() == del.size());
 54         }
 55 }Heap;
 56 
 57 int n, s, t;
 58 Segment* ss;
 59 vector<Segment*> *g;
 60 Heap h;
 61 ll *f;
 62 
 63 inline void init() {
 64     scanf("%d%d%d", &n, &s, &t);
 65     ss = new Segment[(n + 1)];
 66     g = new vector<Segment*>[(t - s + 5)]; 
 67     f = new ll[(t - s + 5)];
 68     t = t - s + 1;
 69     for (int i = 1; i <= n; i++) {
 70         scanf("%d%d%d", &ss[i].l, &ss[i].r, &ss[i].w);
 71         ss[i].l = max(ss[i].l - s, 0) + 1;
 72         ss[i].r = max(ss[i].r - s, 0) + 1;
 73         if (ss[i].r > t)    ss[i].r = t;
 74     }
 75 }
 76 
 77 inline void solve() {
 78     int p = 1;
 79     f[0] = 0;
 80     sort (ss + 1, ss + n + 1);
 81     for (int i = 1; i <= t; i++) {
 82         while (p <= n && ss[p].l == i) {
 83             h.push(f[ss[p].l - 1] + ss[p].w);
 84             g[ss[p].r].push_back(ss + p);
 85             p++;
 86         }
 87         if (h.empty()) {
 88             puts("-1");
 89             return;
 90         }
 91         f[i] = h.top();
 92         for (int j = 0; j < (signed)g[i].size(); j++)
 93             h.remove(f[g[i][j]->l - 1] + g[i][j]->w);
 94         g[i].clear();
 95     }
 96     printf(Auto"\n", f[t]);
 97 }
 98 
 99 int main() {
100     init();
101     solve();
102     return 0;
103 }
Solution 1

Solution#2 Shortest Path & Segment Tree Optimization

  其实把dp的转移变成边,然后发现巧的是每条区间的前一个点,向区间上的所有整点连边。

  于是写个线段树建图优化(其实没什么高端的,就是建虚点,防止重复的连边,然后再加点连边的技巧就好了)。

Code

  1 /**
  2  * Vijos
  3  * Problem#1404
  4  * Accepted
  5  * Time: 606ms
  6  * Memory: 16.0m
  7  */
  8 #include <bits/stdc++.h>
  9 #ifndef WIN32
 10 #define Auto "%lld"
 11 #else
 12 #define Auto "%I64d"
 13 #endif
 14 using namespace std;
 15 typedef bool boolean;
 16 
 17 #define ll long long
 18 #define pii pair<int, int>
 19 #define fi first
 20 #define sc second
 21 
 22 const signed ll llf = (signed ll) (~0ull >> 1);
 23 
 24 int n, s, t;
 25 vector<pii> *g;
 26 ll *f;
 27 boolean *vis;
 28 
 29 inline void init() {
 30     scanf("%d%d%d", &n, &s, &t);
 31     t = t - s + 1;
 32     int siz = ((t + 1) << 2) + 5;
 33     g = new vector<pii>[siz];
 34     f = new ll[siz];
 35     vis = new boolean[siz];
 36     fill(vis, vis + siz, false);
 37     fill(f, f + siz, llf >> 1);
 38 }
 39 
 40 void build(int p, int l, int r) {
 41     if (l == r)    return;
 42     int mid = (l + r) >> 1;
 43     g[p].push_back(pii(p << 1, 0));
 44     g[p].push_back(pii((p << 1) | 1, 0));
 45     build(p << 1, l, mid);
 46     build((p << 1) | 1, mid + 1, r);
 47 }
 48 
 49 int query(int p, int l, int r, int idx) {
 50     if (l == r)
 51         return p;
 52     int mid = (l + r) >> 1;
 53     if (idx <= mid)
 54         return query(p << 1, l, mid, idx);
 55     return query((p << 1) | 1, mid + 1, r, idx);
 56 }
 57 
 58 void query(int p, int l, int r, int ql, int qr, int st, int w) {
 59     if (l == ql && r == qr)    {
 60         g[st].push_back(pii(p, w));
 61         return;
 62     }
 63     int mid = (l + r) >> 1;
 64     if (qr <= mid)
 65         query(p << 1, l, mid, ql, qr, st, w);
 66     else if (ql > mid)
 67         query((p << 1) | 1, mid + 1, r, ql, qr, st, w);
 68     else {
 69         query(p << 1, l, mid, ql, mid, st, w);
 70         query((p << 1) | 1, mid + 1, r, mid + 1, qr, st, w);
 71     }
 72 }
 73 
 74 inline void build() {
 75     build(1, 0, t);
 76     for (int i = 1, l, r, w; i <= n; i++) {
 77         scanf("%d%d%d", &l, &r, &w);
 78         l = max(l - s, 0) + 1;
 79         r = max(r - s, 0) + 1;
 80         if (r > t)    r = t;
 81         int id = query(1, 0, t, l - 1);
 82         query(1, 0, t, l, r, id, w);
 83     }
 84 }
 85 
 86 queue<int> que;
 87 inline void spfa(int s) {
 88     que.push(s);
 89     f[s] = 0;
 90     while (!que.empty()) {
 91         int e = que.front();
 92         que.pop();
 93         vis[e] = false;
 94         for (int i = 0; i < (signed)g[e].size(); i++) {
 95             int eu = g[e][i].fi, w = g[e][i].sc;
 96             if (f[e] + w < f[eu]) {
 97                 f[eu] = f[e] + w;
 98                 if (!vis[eu]) {
 99                     vis[eu] = true;
100                     que.push(eu);
101                 }
102             }
103         }
104     }
105 }
106 
107 inline void solve() {
108     int is = query(1, 0, t, 0);
109     int it = query(1, 0, t, t);
110     spfa(is);
111     if (f[it] == (llf >> 1))
112         puts("-1");
113     else
114         printf(Auto"\n", f[it]);
115 }
116 
117 int main() {
118     init();
119     build();
120     solve();
121     return 0;
122 }
Solution 2

Solution#3 Shortest Path

  wtf?纯最短路?没逗我?

  每个区间的前一个点向区间结束点连边。然后每个点向它的前一个点连边,权值为0。

  (神奇的建图方法。。)

  看起来非常朴素,竟然没有dp快

Code

 1 /**
 2  * Vijos
 3  * Problem#1404
 4  * Accepted
 5  * Time: 285ms
 6  * Memory: 5.75m
 7  */
 8 #include <bits/stdc++.h>
 9 #ifndef WIN32
10 #define Auto "%lld"
11 #else
12 #define Auto "%I64d"
13 #endif
14 using namespace std;
15 typedef bool boolean;
16 
17 #define ll long long
18 #define pii pair<int, int>
19 #define fi first
20 #define sc second
21 
22 const signed ll llf = (signed ll) (~0ull >> 1);
23 
24 int n, s, t;
25 vector<pii> *g;
26 ll *f;
27 boolean *vis;
28 
29 inline void init() {
30     scanf("%d%d%d", &n, &s, &t);
31     t = t - s + 1;
32     g = new vector<pii>[t + 1];
33     f = new ll[t + 1];
34     vis = new boolean[t + 1];
35     fill(vis, vis + t + 1, false);
36     fill(f, f + t + 1, llf >> 1);
37 }
38 
39 inline void build() {
40     for (int i = 1, l, r, w; i <= n; i++) {
41         scanf("%d%d%d", &l, &r, &w);
42         l = max(l - s, 0) + 1;
43         r = max(r - s, 0) + 1;
44         if (r > t)    r = t;
45         g[l - 1].push_back(pii(r, w));
46     }
47     for (int i = 1; i <= t; i++)
48         g[i].push_back(pii(i - 1, 0));
49 }
50 
51 queue<int> que;
52 inline void spfa(int s) {
53     que.push(s);
54     f[s] = 0;
55     while (!que.empty()) {
56         int e = que.front();
57         que.pop();
58         vis[e] = false;
59         for (int i = 0; i < (signed)g[e].size(); i++) {
60             int eu = g[e][i].fi, w = g[e][i].sc;
61             if (f[e] + w < f[eu]) {
62                 f[eu] = f[e] + w;
63                 if (!vis[eu]) {
64                     vis[eu] = true;
65                     que.push(eu);
66                 }
67             }
68         }
69     }
70 }
71 
72 inline void solve() {
73     spfa(0);
74     if (f[t] == (llf >> 1))
75         puts("-1");
76     else
77         printf(Auto"\n", f[t]);
78 }
79 
80 int main() {
81     init();
82     build();
83     solve();
84     return 0;
85 }
posted @ 2018-02-06 10:19  阿波罗2003  阅读(386)  评论(0编辑  收藏  举报