1. 题目描述
有$k$个伞兵跳伞,有$m$个汇点。当伞兵着陆后,需要走向离他最近的汇点。如何选择这$m$个结点,可以使得士兵最终行走的距离的期望最小。
求这个最小的期望。
2. 基本思路
假设已经选好了这$m$个汇点,这也就确定了每个士兵的可能着陆点需要继续行走的距离。因此,这个期望为
egin{align}
E_{min} &= sum_{i=1}^{k} sum_{j=1}^{L_i} minDis(X_{ij}) * P_{ij}
end{align}
显然这等价于
egin{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的最小期望值。显然有状态转移方程
egin{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。有如下推导
egin{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 - 2Delta 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 ", 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. ", (int)clock()); 146 #endif 147 148 return 0; 149 }