AcWing 348. 沙漠之王(0/1分数规划)

0/1分数规划

从该题可以归纳出的0/1分数规划的一般模型:给定正整数 a 1 , a 2 . . . a n a_{1},a_{2}...a_{n} a1,a2...an以及 b 1 , b 2 . . . b n b_{1}, b_{2}...b_{n} b1,b2...bn从中选出若干对的a和b的和的商,求商的最大值Max或者最小值Min,即:
∑ a [ i ] ∑ b [ i ] ∈ [ M i n ,   M a x ] \frac{\sum_{}a[i]}{\sum_{}b[i]}∈[Min, \ Max] b[i]a[i][Min, Max]

由于不知道他们的商是多少,我们不妨先假设 ∑ a [ i ] ∑ b [ i ] = m \frac{\sum_{}a[i]}{\sum_{}b[i]}=m b[i]a[i]=m

find_Min

1.若 任意 ∑ ( a [ i ] − m ∗ b [ i ] ) ≥ 0 \sum (a[i] - m * b[i]) ≥ 0 (a[i]mb[i])0 ∑ ( a [ i ] − m ∗ b [ i ] ) m i n ≥ 0 \sum (a[i] - m * b[i])_{min}≥ 0 (a[i]mb[i])min0 ( ∑ a [ i ] ∑ b [ i ] ) m i n ≥ m (\frac{\sum a[i]}{\sum b[i]})_{min} ≥ m (b[i]a[i])minm
M i n ≥ m Min ≥ m Minm
2.若 存在 ∑ ( a [ i ] − m ∗ b [ i ] ) < 0 \sum (a[i] - m * b[i]) < 0 (a[i]mb[i])<0 ∑ ( a [ i ] − m ∗ b [ i ] ) m i n < 0 \sum (a[i] - m * b[i])_{min}< 0 (a[i]mb[i])min<0 ( ∑ a [ i ] ∑ b [ i ] ) m i n < m (\frac{\sum a[i]}{\sum b[i]})_{min} < m (b[i]a[i])min<m
M i n < m Min < m Min<m

find_Max

1.若 存在 ∑ ( a [ i ] − m ∗ b [ i ] ) ≥ 0 \sum (a[i] - m * b[i]) ≥ 0 (a[i]mb[i])0 ∑ ( a [ i ] − m ∗ b [ i ] ) m a x ≥ 0 \sum (a[i] - m * b[i])_{max}≥ 0 (a[i]mb[i])max0 ( ∑ a [ i ] ∑ b [ i ] ) m a x ≥ m (\frac{\sum a[i]}{\sum b[i]})_{max} ≥ m (b[i]a[i])maxm
M a x ≥ m Max ≥ m Maxm
2.若 任意 ∑ ( a [ i ] − m ∗ b [ i ] ) < 0 \sum (a[i] - m * b[i]) < 0 (a[i]mb[i])<0 ∑ ( a [ i ] − m ∗ b [ i ] ) m a x < 0 \sum (a[i] - m * b[i])_{max}< 0 (a[i]mb[i])max<0 ( ∑ a [ i ] ∑ b [ i ] ) m a x < m (\frac{\sum a[i]}{\sum b[i]})_{max} < m (b[i]a[i])max<m
M a x < m Max < m Max<m

二分

综上所诉,我们可以二分查找m
1.当 ∑ a [ i ] ∑ b [ i ] ≥ m \frac{\sum a[i]}{\sum b[i]} ≥ m b[i]a[i]m,即m ≤ {Max or Min},→ l = mid;
2.当 ∑ a [ i ] ∑ b [ i ] < m \frac{\sum a[i]}{\sum b[i]} < m b[i]a[i]<m,即m > {Max or Min},→ r = mid;


348. 沙漠之王

在这里插入图片描述
思路:
成本为c, 长度为v
1.题意求生成树的 ∑ c [ i ] ∑ v [ i ] \frac{\sum c[i]}{\sum v[i]} v[i]c[i]的Min, 不妨设 ∑ c [ i ] ∑ v [ i ] = m \frac{\sum c[i]}{\sum v[i]} = m v[i]c[i]=m
2.若 任意 ∑ ( c [ i ] − m ∗ v [ i ] ) ≥ 0 \sum (c[i] - m * v[i]) ≥ 0 (c[i]mv[i])0
⇔ 以 ( c [ i ] − m ∗ v [ i ] ) (c[i] - m * v[i]) (c[i]mv[i])为边权的最小生成树
∑ ( c [ i ] − m ∗ v [ i ] ) m i n ≥ 0 \sum (c[i] - m * v[i])_{min}≥ 0 (c[i]mv[i])min0
( ∑ c [ i ] ∑ v [ i ] ) m i n ≥ m (\frac{\sum c[i]}{\sum v[i]})_{min} ≥ m (v[i]c[i])minm
即 m 小于等于Min
则 l = mid 反正则 r = mid
3. 稠密图用prim算法 O ( n 2 ) O(n^2) O(n2), 用Kruskal O ( m l o g m ) O(mlogm) O(mlogm) , m = n 2 m = n ^ 2 m=n2 会超时

样例输入:

4
0 0 0
0 1 1
1 1 2
1 0 3
0

样例输出:

1.000

代码:

//最优比例生成树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1005, M = 1e6 + 10, INF = 10000005;
typedef pair<int, int> PII;
int n;
double mm;

double c[N][N], v[N][N], dist[N];
bool st[N];

struct Point{
    int x, y, d;
}point[N];


double dis(Point a, Point b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}


double prim(double m)
{
    for(int i = 1; i <= n; i ++ ) dist[i] = INF;
    memset(st, false, sizeof st);
    double sum = 0;
    for(int i = 0; i < n; i ++ )
    {
        int t = -1;
        for(int j = 1; j <= n; j ++ )
            if(!st[j] && (t == -1 || dist[j] < dist[t]))
                t = j;
        if(i) sum += dist[t];
        for(int j = 1; j <= n; j ++ )
        {
            double tmp = c[t][j] - v[t][j] * m;
            if(!st[j]) dist[j] = min(dist[j], tmp);
        }
        st[t] = true;
    }
    return sum;
}

int main()
{
    while(cin >> n)
    {
        if(n == 0) break;
        for(int i = 1; i <= n; i ++ )
        {
            int x, y, d;
            cin >> x >> y >> d;
            point[i] = {x, y, d};
        }
        
        for(int i = 1; i <= n; i ++ )
            for(int j = i + 1; j <= n; j ++ )
            {
                c[i][j] = c[j][i] = (double)abs(point[i].d - point[j].d);
                v[i][j] = v[j][i] = dis(point[i], point[j]);
            }
        
        double l = 0, r = 100;
        while(r - l >= 1e-4)
        {
            double mid = (l + r) / 2.0;
            if(prim(mid) >= 0) l = mid;
            else r = mid;
        }
        printf("%.3lf\n", r);        
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值