Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 数论

  • 动态规划

  • 暴力

  • 图论

  • 数据结构

  • 字符串

  • 几何

  • 博弈论

  • 其他

    • 01分数规划学习笔记
      • 例题 POJ 2728
    • CDQ分治学习笔记
    • wqs二分学习笔记
    • 一两句话小技巧
    • 单调性应用学习笔记
    • 哈希函数(Hash)函数的设计
    • 带权并查集处理线段
    • 环检测算法学习笔记
    • 约瑟夫环学习笔记
    • 组合情况
    • 高效位运算

01分数规划学习笔记

# 01分数规划

求解∑AiXi∑BiXi(Xi∈{0,1})\frac{\sum{A_iX_i}}{\sum{B_iX_i}}\ (X_i\in\{0,1\})∑Bi​Xi​∑Ai​Xi​​(Xi​∈{0,1})的最小值,并且有某些奇怪的限制。

我们把Xi=1X_i=1Xi​=1的提取出来

我们可以二分一个答案midmidmid,如果答案的最小值小于等于midmidmid,那么有∑Ai∑Bi≤mid\frac{\sum A_i}{\sum B_i}\le mid∑Bi​∑Ai​​≤mid

可以化简为∑{Ai−mid∗Bi}≤0\sum \{A_i - mid*B_i\} \le 0∑{Ai​−mid∗Bi​}≤0,只需判断是否有符合条件的Ai、BiA_i、B_iAi​、Bi​序列即可

# 例题 POJ 2728

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

using namespace std;

const int N = 1020;
const double eps = 1e-5; // 开太大会WA,太小(如1e-6)会TLE
double dist[N][N], cur_dist[N], x[N], y[N], w[N];
bool vis[N];
int n;

inline double calc_dist(int i, int j){
    return sqrt((x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) * (y[i]-y[j]));
}

double prim(double ans){
    memset(vis, 0, sizeof vis);
    vis[0] = 1;
    double sum = 0;
    for(int i=1; i<n; i++) cur_dist[i] = fabs(w[0]-w[i]) - ans*dist[0][i];
    for(int i=1; i<n; i++){
        int most = -1;
        double extreme;
        for(int j=1; j<n; j++){
            if(vis[j]) continue;
            if(most == -1 || extreme > cur_dist[j]){
                most = j;
                extreme = cur_dist[j];
            }
        }
        vis[most] = 1;
        sum += cur_dist[most];
        for(int j=1; j<n; j++){
            if(vis[j]) continue;
            cur_dist[j] = min(cur_dist[j], fabs(w[j]-w[most]) - ans*dist[j][most]);
        }
    }
    return sum;
}

void solve(){
    for(int i=0; i<n; i++) scanf("%lf%lf%lf", &x[i], &y[i], &w[i]);
    for(int i=0; i<n; i++){
        for(int j=i; j<n; j++)
            dist[i][j] = dist[j][i] = calc_dist(i, j);
    }
    double l=0, r=100, mid; // 理论上r=100是不对的,但是这题开太大会TLE
    while(r-l > eps){
        mid = (l + r) / 2;
        if(prim(mid) <= 0){
            r = mid;
        }else{
            l = mid;
        }
    }
    printf("%.3f\n", l); // 很邪门,用%.3lf会WA
}

signed main(){
    while(scanf("%d", &n) == 1 && n) solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
上次更新: 2021/02/24, 03:37:30
威佐夫博弈
CDQ分治学习笔记

← 威佐夫博弈 CDQ分治学习笔记→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式