很早就想学的东西了……但是要学的实在是太多了……
01分数规划是分数规划中最简单的,所谓01,就是对于每一件物品,他都是不同的,你只有取或者不取两种选择,没有在数量上太多的选择。就像01背包一样。
01分数规划 虽然考察的不多,套路也比较死,但是多校的时候着实出现过。记得应该是cls的那一场,记得是01分数规划的新操作……
01分数规划是用来解决取舍导致的比例最值问题。解决该问题的方法有两种。
一种是二分,另一种是迭代,迭代法也称 Dinkelbach 算法。
二分法基于验证,迭代法基于直接计算,一般来说迭代法会更快一些。
两者写起来都非常简单。
入门资料推荐。具体思路不再细说。
入门题在这里。
入门资料中谈到,如果对于题目改成最小化,那么就尽量少选,不是很理解他的说法。
就我看来,把分子分母倒一下就转换过来了。
题意:
最裸的01分数规划,要你从 n 个中挑出 k 个使得比例最大。
就图片来说就是要你求这样的结果,当然是选取 k 个。
思路:
验证板子的题,因为它的限制只有一个,没什么好说的。看了上面的入门资料就很好理解的。
01分数规划可怕的是大量的限制。考虑的真的是脑壳疼。
这里贴出两种解法的代码。
Dinkelbach
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#define each(i, n) for (int(i) = 0; (i) < (n); (i)++)
#define reach(i, n) for (int(i) = n - 1; (i) >= 0; (i)--)
#define range(i, st, en) for (int(i) = (st); (i) <= (en); (i)++)
#define rrange(i, st, en) for (int(i) = (en); (i) >= (st); (i)--)
#define fill(ary, num) memset((ary), (num), sizeof(ary))
using namespace std;
const double eps = 1e-5;
const int maxn = 1e3 + 5;
int n, k;
double a[maxn], b[maxn], d[maxn];
int id[maxn];
bool cmp(int a, int b) { return d[a] > d[b]; }
double Dinkelbach()
{
double ans = 0, L, x, y;
do {
L = ans, x = 0, y = 0;
each(i, n) d[i] = a[i] - L * b[i];
each(i, n) id[i] = i;
sort(id, id + n, cmp);
each(i, n - k) x += a[id[i]], y += b[id[i]];
ans = 1.0 * x / y;
} while (fabs(ans - L) > eps);
return ans;
}
int main()
{
while (scanf("%d %d", &n, &k) != EOF && n + k) {
each(i, n) scanf("%lf", a + i);
each(i, n) scanf("%lf", b + i);
printf("%.0f\n", Dinkelbach() * 100);
}
return 0;
}
二分
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define each(i, n) for (int(i) = 0; (i) < (n); (i)++)
#define reach(i, n) for (int(i) = n - 1; (i) >= 0; (i)--)
#define range(i, st, en) for (int(i) = (st); (i) <= (en); (i)++)
#define rrange(i, st, en) for (int(i) = (en); (i) >= (st); (i)--)
#define fill(ary, num) memset((ary), (num), sizeof(ary))
using namespace std;
const double eps = 1e-5;
const int maxn = 1e3 + 5;
int n, k;
double a[maxn], b[maxn], d[maxn];
bool judge(double key)
{
each(i, n) d[i] = a[i] - key * b[i];
sort(d, d + n, greater<double>());
double sum = 0;
each(i, n - k) sum += d[i];
return sum - eps > 0;
}
int main()
{
while (scanf("%d %d", &n, &k) != EOF && n + k) {
each(i, n) scanf("%lf", a + i);
each(i, n) scanf("%lf", b + i);
double l = 0, r = 1e12, mid;
while (r - l > eps) {
mid = (l + r) / 2;
if (judge(mid))
l = mid;
else
r = mid - eps;
}
printf("%.0f\n", l * 100);
}
return 0;
}