每日一记:分数规划

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

分数规划一般形式:

  

解向量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);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值