Desert King(01分数规划问题)(最优斜率生成树)

Desert King

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions:33847   Accepted: 9208

Description

David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way.

After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital.

His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can‘t share a lifter. Channels can intersect safely and no three villages are on the same line.

As King David‘s prime scientist and programmer, you are asked to find out the best solution to build the channels.

Input

There are several test cases. Each test case starts with a line containing a number N (2 <= N <= 1000), which is the number of villages. Each of the following N lines contains three integers, x, y and z (0 <= x, y < 10000, 0 <= z < 10000000). (x, y) is the position of the village and z is the altitude. The first village is the capital. A test case with N = 0 ends the input, and should not be processed.

Output

For each test case, output one line containing a decimal number, which is the minimum ratio of overall cost of the channels to the total length. This number should be rounded three digits after the decimal point.

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000

题意

有带权图G, 对于图中每条边e[i], 都有benifit[i](收入)和cost[i](花费), 我们要求的是一棵生成树T, 它使得 ∑(benifit[i]) / ∑(cost[i]), i∈T 最大(或最小).

这显然是一个具有现实意义的问题.

题解

解法之一 0-1分数规划

设x[i]等于1或0, 表示边e[i]是否属于生成树.

则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<m .

为了使 r 最大, 设计一个子问题---> 让 z = ∑(benifit[i] * x[i]) - l * ∑(cost[i] * x[i]) = ∑(d[i] * x[i]) 最大 (d[i] = benifit[i] - l * cost[i]) , 并记为z(l). 我们可以兴高采烈地把z(l)看做以d为边权的最大生成树的总权值.

然后明确两个性质:

 1.  z单调递减

  证明: 因为cost为正数, 所以z随l的减小而增大.

 2.  z( max(r) ) = 0

  证明: 若z( max(r) ) < 0, ∑(benifit[i] * x[i]) - max(r) * ∑(cost[i] * x[i]) < 0, 可化为 max(r) < max(r). 矛盾;

          若z( max(r) ) >= 0, 根据性质1, 当z = 0 时r最大.

到了这个地步, 七窍全已打通, 喜欢二分的上二分, 喜欢Dinkelbach的就Dinkelbach.

复杂度

时间 O( O(MST) * log max(r) )

空间 O( O(MST) )

C++代码

二分法

/*
*@Author:   Agnel-Cynthia
*@Language: C++
*/
//#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=1000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
//int t,n,m,k,p,l,r,u,v;
const int maxn = 1005;
//ll a[maxn],b[maxn];

struct node
{
    int x , y ,z ;
}edge[maxn];

int n ;

double mp[maxn][maxn];

double dis(double x1 ,double y1,double x2,double y2){
    return sqrt(1.0*(x1-x2) * (x1 - x2) + 1.0 * (y1 - y2) * (y1 - y2));
}

void creat(){
    for(int i = 1;i <= n ;i ++){
        for(int j = 1;j <= n ; j++){
            mp[i][j] = dis(edge[i].x,edge[i].y,edge[j].x,edge[j].y);
        }
    }
}

double d[maxn];
bool vis[maxn];

double prime(double mid){
    memset(vis,0,sizeof vis);
    for(int i = 1;i <= n ; i++){
        d[i] = abs(edge[1].z - edge[i].z) - mp[1][i] * mid;
    }
    vis[1] = true;
    double ans = 0;
    for(int i = 1;i < n ; i++){
        int v = -1;double MIN = INF;
        for(int j = 1;j <= n ; j++){
            if(MIN >= d[j] && !vis[j]){
                v = j;
                MIN = d[j];
            }
        }
        if(v == -1)
            break;
        vis[v] = true;
        ans += MIN;
        for(int j = 1;j <= n ; j++){
            if(!vis[j] && (fabs(edge[v].z - edge[j].z) - mp[v][j] * mid) < d[j])
                d[j] = (fabs(edge[v].z - edge[j].z) - mp[v][j] * mid);
        }
    }
    return ans ;
}

int main()
{
#ifdef DEBUG
    freopen("input.in", "r", stdin);
    //freopen("output.out", "w", stdout);
#endif
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    while(cin >> n && n){
        for(int i = 1;i <= n ; i++){
            cin >> edge[i].x >> edge[i].y >> edge[i].z;
        }
        double  l = 0, r = 40.0;
        double mid = 0;
        creat();
        while(fabs(r - l) > EXP){
            mid = (l + r) / 2;
            if(prime(mid) >= 0)
                l = mid;
            else
                r = mid;
        }
        printf("%.3lf\n",mid );
    }
#ifdef DEBUG
    printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}

二分法与Dinkelbach算法

Dinkelbach

/*
*@Author:   Agnel-Cynthia
*@Language: C++
*/
//#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=1000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
//int t,n,m,k,p,l,r,u,v;
//ll a[maxn],b[maxn];

#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)

int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<‘!‘) ;
if (CH == ‘-‘) NEG = true , CH = getchar() ;
while (ret = ret*10+CH-‘0‘ , CH=getchar() , CH>‘!‘) ;
if (NEG) ret = -ret ;
}
#define  maxn  1010LL
#define  infi  100000000LL
#define  eps   1E-8F
#define  sqr(x) ((x)*(x))

template <typename TP>inline bool MA(TP&a,const TP&b) { return a < b ? a = b, true : false; }
template <typename TP>inline bool MI(TP&a,const TP&b) { return a > b ? a = b, true : false; }

int n;
int x[maxn], y[maxn], h[maxn];
double v[maxn][maxn], c[maxn][maxn];

bool vis[maxn];
double w[maxn];
double rv[maxn];///
inline double prim(double M) {
    int i, j, k;
    double minf, minw;
double sumc = 0, sumv = 0;///
memset(vis,0,sizeof vis);
Rep (i,2,n) w[i] = v[1][i]-M*c[1][i],
rv[i] = v[1][i];///
vis[1] = true, minf = 0;
rep (i,1,n) {
    minw = infi;
    Rep (j,1,n) if (!vis[j] && w[j]<minw)
    minw = w[j], k = j;
sumv += rv[k], sumc += rv[k]-w[k];///
minf += minw, vis[k] = true;
Rep (j,1,n) if (!vis[j])
if (MI(w[j],v[k][j]-M*c[k][j]))
rv[j] = v[k][j];///
}
return sumv*M/sumc;///
return minf;
}

int main() {
    int i, j;
    double L, M, R;
    double maxv, maxc, minv, minc;
    while (scanf("%d", &n)!=EOF && n) {
        Rep (i,1,n)
        scanf("%d%d%d", &x[i], &y[i], &h[i]);
        maxv = maxc = -infi, minv = minc = infi;
        rep (i,1,n) Rep (j,i+1,n) {
            c[i][j] = c[j][i] = sqrt(sqr((double)x[i]-x[j])+sqr((double)y[i]-y[j]));
            v[i][j] = v[j][i] = abs((double)h[i]-h[j]);
            MA(maxv,v[i][j]), MI(minv,v[i][j]);
            MA(maxc,c[i][j]), MI(minc,c[i][j]);
        }
        L = minv/maxc, R = maxv/minc;
while (true) {///
    R = prim(L);///
    if (fabs(L-R) < eps) break;///
    L = R;///
}///
        /*while (R-L > 1E-6) { // L:minf>0  R:minf<=0
            M = (L+R)/2.0;
            if (prim(M) > eps) L = M;
            else R = M;
        }*/
printf("%.3f\n", R);
}
//END: getchar(), getchar();
return 0;
}

原文地址:https://www.cnblogs.com/DWVictor/p/11231535.html

时间: 2024-07-30 12:30:00

Desert King(01分数规划问题)(最优斜率生成树)的相关文章

poj2728 Desert King --- 01分数规划 二分水果。。

这题数据量较大,普通的求MST是会超时的. d[i]=cost[i]-ans*dis[0][i] 据此二分. 但此题用Dinkelbach迭代更好 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; #define N 1010 double mp[N][N],c[N][N],x

POJ 2728 Desert King (01分数规划)

Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions:29775   Accepted: 8192 Description David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his count

POJ 2728 Desert King 0-1分数规划

题意:链接 方法: 0-1分数规划 解析: 这是之前没填的坑,现在来填坑. 这道题题意就是n个三维坐标系的点,任意两点都可以连边,每条边的花费是两点在xOy坐标系下的欧几里得距离,每条边的收益是两点的z值差的绝对值. n个点连成一棵树 求最小的花费比收益. 即求最大的收益比花费. 一看求的东西就可以考虑0-1分数规划咯? 所以二分那个L,然后每条边的get-L*cost就是每条边新的权值,因为要拿最大的n-1条,所以上MST,但是这题是n^2的边,kruscal的话是n^2logn^2*log(

poj2728 Desert King——01分数规划

题目:http://poj.org/problem?id=2728 第一道01分数规划题!(其实也蛮简单的) 这题也可以用迭代做(但是不会),这里用了二分: 由于比较裸,不作过多说明了. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define eps 1e-6 using namespace std; int const inf=0x3f3f3f;

$POJ$2728 $Desert\ King$ 01分数规划

正解:01分数规划 解题报告: 传送门! 感觉挺板子的,,, 因为还没做,先瞎口胡一个做法$QAQ$ 如果翻车了请当做没看见我如果错了等$get$正确做法会重新编辑的$QAQ$ 就因为有$n\leq 100$,于是如果把它联成一个完全图边数连$1e4$都没有 所以考虑直接连成一个完全图,就成了之前寒假考试题二分专题那次的$T1$辽,就直接无脑01分数规划+最大生成树就欧克 $over$? 原文地址:https://www.cnblogs.com/lqsukida/p/10827485.html

POJ 3621 Sightseeing Cows 01分数规划,最优比例环的问题

http://www.cnblogs.com/wally/p/3228171.html 题解请戳上面 然后对于01规划的总结 1:对于一个表,求最优比例 这种就是每个点位有benefit和cost,这样就是裸的01规划 2:对于一个树,求最优比例 这种就是每条边有benefit和cost,然后通过最小生成树来判断 3:对于一个环求最优比例 这种也是每条边有benefit和cost,然后通过spfa来判断 其实01规划最核心的地方,在于构建01规划函数,构建好函数,然后根据单调性,判断大于0或者小

01分数规划(转)

01分数规划 分类: DP&&记忆化搜索2013-05-04 14:47 4193人阅读 评论(1) 收藏 举报 [关键字] 0/1分数规划.最优比率生成树.最优比率环 [背景] 根据楼教主的回忆录,他曾经在某一场比赛中秒掉了一道最优比率生成树问题,导致很多人跟风失败,最终悲剧.可见最优比率生成树是多么凶残的东西,但是这个东西只要好好研究半天就可以掌握,相信你在看了我写的这篇总结之后可以像楼教主一般秒掉这类问题. 因为网上对于01分数规划问题的详细资料并不是太多,所以我就结合自己的一些理解

【转】[Algorithm]01分数规划

因为搜索关于CFRound277.5E题的题解时发现了这篇文章,很多地方都有值得借鉴的东西,因此转了过来 原文:http://www.cnblogs.com/perseawe/archive/2012/05/03/01fsgh.html [关键字] 0/1分数规划.最优比率生成树.最优比率环 [背景] 根据楼教主的回忆录,他曾经在某一场比赛中秒掉了一道最优比率生成树问题,导致很多人跟风失败,最终悲剧. 自己总结了一些这种问题的解法,因为水平有限,如果有错误或是麻烦的地方,尽管喷,邮箱或是下方留言

[POJ 2728]Desert King(0-1分数规划/最优比率生成树)

Description David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be

poj 2728 Desert King(最优比率生成树,01分数规划)

http://poj.org/problem?id=2728 大致题意:有n个村庄,输入每个村庄的位置和高度,这n个村庄要连在一起,村与村之间的长度为他们之间的欧几里得距离,花费是两村之间的高度差,要求连在一起的花费和与距离和之比的最小值. 思路:明显的最优比率生成树.二分答案λ,每条边重新赋权c[i] - λd[i] ,因为要求比值最小,那么对于所有的生成树,它们的f[λ]必须>=0,所以只需求得基于最小生成树的f'[λ],当f'[λ] = 0时即找到了正解λ*. 二分: #include <