【HDOJ】4412 Sky Soldiers

1. 题目描述
有$k$个伞兵跳伞,有$m$个汇点。当伞兵着陆后,需要走向离他最近的汇点。如何选择这$m$个结点,可以使得士兵最终行走的距离的期望最小。
求这个最小的期望。

2. 基本思路
假设已经选好了这$m$个汇点,这也就确定了每个士兵的可能着陆点需要继续行走的距离。因此,这个期望为
\begin{align}
    E_{min} &= \sum_{i=1}^{k} \sum_{j=1}^{L_i} minDis(X_{ij}) * P_{ij}
\end{align}
显然这等价于
\begin{align}
    xst &= set(X), nx = |xst|   \\
    E_{min} &= \sum_{i=1}^{nx} minDis(xst_i) * \sum_{i=1}^{k} \sum_{j=1}^{L_i} (X_{ij}==xst_i) * P{ij}  
\end{align}
这也就意味着,最终的最小期望值与$k$大小无关,与$nx$大小有关,$\sum_{i=1}^{k} \sum_{j=1}^{L_i} (X_{ij}==xst_i) * P{ij} $其实是针对某个x的累加概率和。
而$nx \leq 1000$。因此,不妨设$dp[i][j]$表示当仅仅有i个汇点时,前j个x的最小期望值。显然有状态转移方程
\begin{align}
    dp[i][j] &= \min (dp[i-1][k] + cost[k+1][j]), k \in [1, j)
\end{align}
关键就是求$cost[i][j]$,表示区间$[i,j]$内的最小费用(仅包含1个汇点)。
而这显然是一个区间DP。有如下推导
\begin{align*}
    cost_k  &= \sum_{i=1}^{k} (x_k-x_i) \cdot P_i + \sum_{i=k+1}^{n} (x_i-x_k) \cdot P_i  \\
    x_{k+1} &= x_k + \Delta \\
    cost_{k+1}  &= \sum_{i=1}^{k+1} (x_{k+1}-x_i) \cdot P_i + \sum_{i=k+2}^{n} (x_i-x_{k+1}) \cdot P_i   \\
            &= \sum_{i=1}^{k+1} (x_k+\Delta-x_i) \cdot P_i + \sum_{i=k+2}^{n} (x_i-x_k-\Delta) \cdot P_i \\
            &= \sum_{i=1}^{k+1} (x_k-x_i) \cdot P_i + \sum_{i=k+2}^{n} (x_i-x_k) \cdot P_i + \Delta (\sum_{i=1}^{k+1}P_i - \sum_{i=k+2}^{n}P_i) \\
            &= \sum_{i=1}^{k} (x_k-x_i) \cdot P_i + \sum_{i=k+1}^{n} (x_i-x_k) \cdot P_i - 2\Delta \cdot P_{k+1} + \Delta (\sum_{i=1}^{k+1}P_i - \sum_{i=k+2}^{n}P_i) \\
            &= cost_k + \Delta (\sum_{i=1}^{k}P_i + P_{k+1} - \sum_{i=k+1}^{n}P_i + P_{k+1} - 2P_{k+1}) \\
            &= cost_k + \Delta (\sum_{i=1}^{k}P_i - \sum_{i=k+1}^{n}P_i)
\end{align*}
这让我在已知$x_k$为$[1,n]$的汇点时,可以快速求出当$x_{k+1}$为汇点时的费用值。$\Delta > 0$恒成立,而$\sum_{i=1}^{k}P_i - \sum_{i=k+1}^{n}P_i$也是单调递增的,因此求最优的$x_k$时可以使用单调性优化。

3. 代码

  1 /* 4412 */
  2 #include <iostream>
  3 #include <sstream>
  4 #include <string>
  5 #include <map>
  6 #include <queue>
  7 #include <set>
  8 #include <stack>
  9 #include <vector>
 10 #include <deque>
 11 #include <bitset>
 12 #include <algorithm>
 13 #include <cstdio>
 14 #include <cmath>
 15 #include <ctime>
 16 #include <cstring>
 17 #include <climits>
 18 #include <cctype>
 19 #include <cassert>
 20 #include <functional>
 21 #include <iterator>
 22 #include <iomanip>
 23 using namespace std;
 24 //#pragma comment(linker,"/STACK:102400000,1024000")
 25 
 26 #define sti                set<int>
 27 #define stpii            set<pair<int, int> >
 28 #define mpii            map<int,int>
 29 #define vi                vector<int>
 30 #define pii                pair<int,int>
 31 #define vpii            vector<pair<int,int> >
 32 #define rep(i, a, n)     for (int i=a;i<n;++i)
 33 #define per(i, a, n)     for (int i=n-1;i>=a;--i)
 34 #define clr                clear
 35 #define pb                 push_back
 36 #define mp                 make_pair
 37 #define fir                first
 38 #define sec                second
 39 #define all(x)             (x).begin(),(x).end()
 40 #define SZ(x)             ((int)(x).size())
 41 #define lson            l, mid, rt<<1
 42 #define rson            mid+1, r, rt<<1|1
 43 
 44 typedef struct node_t {
 45     int x;
 46     double p;
 47     
 48     node_t() {}
 49     node_t(int x, double p):
 50         x(x), p(p) {}
 51         
 52     friend bool operator< (const node_t& a, const node_t& b) {
 53         return a.x < b.x;
 54     }
 55     
 56 } node_t;
 57 
 58 const double INF = 1e18;
 59 const int maxn = 1005;
 60 const int maxm = 55;
 61 double cost[maxn][maxn];
 62 double dp[maxm][maxn];
 63 map<int,double> tb;
 64 map<int,double>::iterator iter;
 65 node_t nd[maxn];
 66 int n, m;
 67 
 68 void init_Cost(int n) {
 69     double mn, tmp;
 70     
 71     rep(i, 0, n) {
 72         int k = i;
 73         double pl = nd[i].p, pr = 0.0;
 74         
 75         mn = 0.0;
 76         cost[i][i] = 0;
 77         
 78         rep(j, i+1, n) {
 79             pr += nd[j].p;
 80             mn += (nd[j].x - nd[k].x) * nd[j].p;
 81             while (k<j && mn > mn + (nd[k+1].x-nd[k].x) * (pl - pr)) {
 82                 mn += (nd[k+1].x-nd[k].x) * (pl - pr);
 83                 ++k;
 84                 pl += nd[k].p;
 85                 pr -= nd[k].p;
 86             }
 87             cost[i][j] = mn;
 88         }
 89     }
 90 }
 91 
 92 void solve() {
 93     int pn = 0;
 94     
 95     for (iter=tb.begin(); iter!=tb.end(); ++iter) {
 96         nd[pn].x = iter->fir;
 97         nd[pn].p = iter->sec;
 98         ++pn;
 99     }
100     
101     sort(nd, nd+pn);
102     
103     init_Cost(pn);
104     
105     rep(j, 0, pn)
106         dp[0][j] = cost[0][j];
107         
108     rep(i, 1, m) {
109         rep(j, 0, pn) {
110             dp[i][j] = dp[i-1][j];
111             rep(k, 0, j)
112                 dp[i][j] = min(dp[i][j], dp[i-1][k]+cost[k+1][j]);
113         }
114     }
115     
116     double ans = INF;
117     ans = min(ans, dp[m-1][pn-1]);
118     printf("%.2lf\n", ans);
119 }
120 
121 int main() {
122     ios::sync_with_stdio(false);
123     #ifndef ONLINE_JUDGE
124         freopen("data.in", "r", stdin);
125         freopen("data.out", "w", stdout);
126     #endif
127     
128     int l;
129     int x;
130     double p;
131     
132     while (scanf("%d %d", &n, &m)!=EOF && (n||m)) {
133         tb.clr();
134         rep(i, 0, n) {
135             scanf("%d", &l);
136             rep(j, 0, l) {
137                 scanf("%d%lf", &x, &p);
138                 tb[x] += p;
139             }
140         }
141         solve();
142     }
143     
144     #ifndef ONLINE_JUDGE
145         printf("time = %d.\n", (int)clock());
146     #endif
147     
148     return 0;
149 }

 

posted on 2016-03-06 14:02  Bombe  阅读(173)  评论(0编辑  收藏  举报

导航