您的位置:首页 > 其它

每日一记:分数规划

2014-08-22 13:06 239 查看
以下部分内容引用论文内容。

分数规划一般形式:



解向量x在解空间S内, a(x)与 b(x)都是连续的实值函数。

求解过程:

1、令g(λ) = min{a(x) -λ*b(x)} (x∈S),g(λ)是一个严格递减的函数

证明:设 λ1 < λ2, x1最小化了g(λ)。则

g(λ1) = min {a(x) - t1*b(x)}

= a(x1) - λ1*b(x1)

> a(x1) - λ2*b(x1) //b(x) > 0

>= min {a(x) - λ2*b(x)} = g(λ2)

Dinkelbach定理:若λ*为原规划的最优解,则g(λ)=0当且仅当λ=λ*。

即只需求出λ*使得g(λ*)=0即可。

2、设λ*为该规划的最优解。



使用二分法可求解。

Example:

Description

Bryan成为了一名土木工程师,负责一个国家的道路建设!这个国家有N个城市,从1到N标号。M条可以修建的路,每条路连接两个城市,并且有修建需要的花费。现在Bryan需要从M条路中选择一些修建,使得国家中任意两个城市都能连通。当然可行方案有很多,Bryan想选择平均修一条路的花费最小的那种方案。如果没有能连通所有城市的方案,输出-1。

Input

第1行: 2个正整数,N M (1<=N<=1000, 0<=M<=100000),N表示城市的数量,M表示可以修建的路的数量。
第2行到第M+1行:每行三个整数,u v c (1<=u,v<=N 0<=c<=1000),表示城市u和城市v之间可以花费费用c修建一条道路。

Output

输出最小的平均花费,四舍五入保留两位小数。如果不存在可行方案,输出-1。
-------------------------------------------------------------------------------------------------------------------

解法:本题可贪心也可分数规划,这里用分数规划求解。
1、设sum为所选总花费,cnt为所选道路数,题目所求为使得sum/cnt最小,满足分数规划的条件、。
2、构造函数g(λ) = min{sum -λ*cnt}。
二分λ:对于λ*,为了求出g(λ*),我们把每条边的边权变为w - λ, 按照此边权求最小生成树即可,注意要把所有负边权也加入最小生成树中,这样总和一定是最小的sum -λ*cnt。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>

using namespace std;

#define MP(x,y)	make_pair(x, y)

const double eps = 1e-3;

int pre[1005], n, m;

void init (){
for (int i = 0; i < 1005; i ++){
pre[i] = i;
}
}

int find (int x){
return pre[x] == x ? x : pre[x] = find (pre[x]);
}

void Union (int x, int y){
pre[find (x)] = find (y);
}

double cal (const vector<pair<int, pair<int, int> > >& g, double t){
init ();
double ans = 0;
int num = 0;
for (int i = 0; i < g.size (); i ++){
int u = g[i].second.first;
int v = g[i].second.second;
int w = g[i].first;
if (find (u) != find (v)){
Union (u, v);
ans += (double)w-t;
num ++;
}else if (w - t < 0){
ans += (w-t);
}
}
if (num < n-1)	return -1;
return ans;
}

int main (){
freopen ("6.in", "r", stdin);
cin >> n >> m;
vector<pair<int, pair<int, int> > > edge;
for (int i = 1; i <= m; i ++){
int u, v, c;
cin >> u >> v >> c;
edge.push_back (MP (c, MP (u, v)));
}
sort (edge.begin (), edge.end ());
if (cal (edge, 0) == -1){
puts ("-1");
return 0;
}
double high = 100000.0, low = 0.0, mid;
while (low + eps < high){
mid = (low + high)/2.0;
if (cal (edge, mid) - eps> 0)	low = mid;
else				high = mid;
}
printf ("%.2f\n", low);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: