**HDU 4412 - Sky Soldiers(区间DP)

题目:

点击打开链接

题意:

有n个飞行员,每个飞行员都有c个落点和相应的概率,要建立m个补给站,使得n个飞行员到达补给站的期望值最小。

思路:

dp[i][j] 表示前i个落点建立j个补给站的最小期望值。

dp[i][j] = min(dp[k][j-1], cost[k][i] ) 。

cost[k][i] 表示区间(k,i)建立1个补给站供给此区间中的飞行员的期望值。

期望值 = (落点- 补给站)* 落点概率。

AC.

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;
const double inf = 1e8*1.0;
const int maxn = 1005;
map<int, double> mp;
double dp[maxn][80], cost[maxn][maxn];

struct node {
    double x, p;
    bool operator < (const node & A) const {
        return x < A.x;
    }
}num[maxn];

int main()
{
    //freopen("in", "r", stdin);
    int n, m;
    while(~scanf("%d%d", &n, &m)) {
        if(n == 0 && m == 0) break;

        mp.clear();
        for(int i = 0; i < n; ++i) {
            int c, x;
            double p;
            scanf("%d", &c);
            while(c--) {
                scanf("%d %lf", &x, &p);
                mp[x] += p;
            }
        }

        int N = 1;
        map<int, double>::iterator it;
        for(it = mp.begin(); it != mp.end(); it++) {
            num[N].x = it->first;
            num[N++].p = it->second;
        }

        sort(num, num+N);

        double fl, fr, pl, pr;
        for(int i = N-1; i >= 1; --i) {
            cost[i][i] = 0;
            fl = fr = pl = pr = 0; // 期望 概率
            pr = num[i].p;
            int pos = i;
            for(int j = i-1; j >= 1; --j) {
                pl += num[j].p;
                fl += num[j].p * (num[pos].x - num[j].x);
                double temp = fl + fr;
                while(temp > fl + fr + (pr - pl)*(num[pos].x - num[pos-1].x) && pos > 1) {
                    pos--;
                    fl -= pl*(num[pos+1].x - num[pos].x);
                    fr += pr*(num[pos+1].x - num[pos].x);
                    pl -= num[pos].p;
                    pr += num[pos].p;
                    temp = fl + fr;
                }
                cost[j][i] = temp;
                //printf("pos = %d, (%d %d) %lf\n", pos+1, j, i, temp);
            }
        }

        for(int i = 0; i < N; ++i) {
            for(int j = 0; j <= m; ++j) dp[i][j] = inf;
        }
        dp[0][0] = 0;
        for(int i = 1; i < N; ++i) {
            for(int j = 1; j <= m; ++j) {
                for(int k = i; k >= 1; --k) {
                    dp[i][j] = min(dp[i][j], dp[k-1][j-1] + cost[k][i]);
                }
            }
        }
        printf("%.2lf\n", dp[N-1][m]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值