做了几天的01分数规划,总结一下吧。个人感觉无论是最优比例生成环还是最优比率生成树或是最小割神马的,基本思路都是一样的即参数搜索,设出一个函数f[λ],每次二分λ,直到f[λ] = 0。而最优比例生成环是基于最短路的01分数规划,最优比率生成树是基于最小生成树的01分数规划,最小割是基于最大流的01分数规划。
http://poj.org/problem?id=2976
大致题意:输入n和k,然后给出n个二元组,要求从中取出n-k个数使得它们 a数之和与b数之和最大。
转载:
题目求的是 max(∑a[i] * x[i] / (b[i] * x[i])) 其中a,b都是一一对应的。 x[i]取0,1 并且 ∑x[i] = n - k;
那么可以转化一下。
令r = ∑a[i] * x[i] / (b[i] * x[i]) 则必然∑a[i] * x[i] - ∑b[i] * x[i] * r= 0;(条件1)
并且任意的 ∑a[i] * x[i] - ∑b[i] * x[i] * max(r) <= 0 (条件2,只有当∑a[i] * x[i] / (b[i] * x[i]) = max(r) 条件2中等号才成立)
然后就可以枚举r , 对枚举的r, 求Q(r) = ∑a[i] * x[i] - ∑b[i] * x[i] * r 的最大值,为什么要求最大值呢?因为我们之前知道了条件2,所以当我们枚举到r为max(r)的值时,显然对于所有的情况Q(r)都会小于等于0,并且Q(r)的最大值一定是0.而我们求最大值的目的就是寻找Q(r)=0的可能性,这样就满足了条件1,最后就是枚举使得Q(r)恰好等于0时就找到了max(r)。而如果能Q(r)>0 说明该r值是偏小的,并且可能存在Q(r)=0,而Q(r)<0的话,很明显是r值偏大的,因为max(r)都是使Q(r)最大值为0,说明不可能存在Q(r)=0了,需要换方向搜索了、
然后算法框架就出来了。
二分枚举r。对每个r。求出每个a[i] - b[i] * r; 然后排序,将最大的n-k个相加即为最Q(r)的最大值。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;
int n,k;
int a[maxn],b[maxn];
double c[maxn];
int main()
{
while(~scanf("%d %d",&n,&k) && (n || k))
{
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
for(int i = 1; i <= n; i++)
scanf("%d",&b[i]);
double l = 0.0;
double r = 1.0;
double mid;
while(fabs(r-l) > eps)
{
mid = (l+r)/2;
for(int i = 1; i <= n; i++)
c[i] = 1.0*a[i] - mid * 1.0 * b[i];
sort(c+1,c+1+n);
double sum = 0.0;
for(int i = k+1; i <= n; i++)
sum += c[i];
if(sum > 0)
l = mid;
else if(sum < 0)
r = mid;
else break;
}
mid = mid*100;
printf("%.0f\n",mid);
}
return 0;
}
Dinkelbach 算法,迭代
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;
int n,k;
int a[maxn],b[maxn];
double c[maxn];
struct node
{
double d;
int c;
bool operator < (const struct node &t)const
{
return d < t.d;
}
}tmp[maxn];
int main()
{
while(~scanf("%d %d",&n,&k))
{
if(n == 0 && k == 0) break;
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
for(int i = 1; i <= n; i++)
scanf("%d",&b[i]);
double l = 0.0,r;
double p,q;
while(1)
{
for(int i = 1; i <= n; i++)
tmp[i] = (struct node){a[i] - l*b[i], i};
sort(tmp+1,tmp+1+n);
p = 0; q = 0;
for(int i = k+1; i <= n; i++)
{
p += a[tmp[i].c];
q += b[tmp[i].c];
}
r = p/q;
if(fabs(r-l) < eps) break;
else l = r;
}
printf("%.0f\n",r*100);
}
return 0;
}