每日一记:分数规划

来源:互联网 发布:数据库中省略为什么 编辑:程序博客网 时间:2024/06/07 01:34

以下部分内容引用论文内容。

分数规划一般形式:

  

解向量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;elsehigh = mid;}printf ("%.2f\n", low);}


0 0