POJ2728 最小比率生成树/0-1分数规划/二分/迭代(迭代不会)

用01分数规划 + prime + 二分 竟然2950MS惊险的过了QAQ

前提是在TLE了好几次下过的 = =

题目意思:
有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小,很显然,这个题目是要求一棵最优比率生成树。

解题思路:

对答案进行二分,当把代进去的答案拿来算最小生成树的时候,一旦总路径长度为0,就是需要的答案。


0-1规划是啥?
 
概念
有带权图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最大.

 

贴代码:

 1 //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler
 2 #include <stdio.h>
 3 #include <iostream>
 4 #include <climits>
 5 #include <cstring>
 6 #include <cmath>
 7 #include <stack>
 8 #include <vector>
 9 #include <algorithm>
10 #define ll long long
11 using namespace std;
12 
13 const int INF = 0x3f3f3f3f;
14 const int MAXN = 1001;
15 struct node{
16     double x,y,h;
17 }dot[MAXN];
18 
19 double map[MAXN][MAXN];
20 int n;
21 double dis(double x1,double y1,double x2,double y2){
22     return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
23 }
24 
25 void creat(int n,double l){
26     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
27         map[i][j]=fabs(dot[i].h-dot[j].h) - l * dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
28 }
29 
30 double prim(){
31     bool vis[MAXN];
32     memset(vis, 0, sizeof(vis));
33     double dis[MAXN];
34     double ans = 0;
35     int i,j;
36     vis[1] = true;
37     for(i = 1; i <= n; ++i)
38         dis[i] = map[1][i];
39     for(i = 1; i < n; ++i){
40         int temp = INF, flag;
41         for(j = 1; j <= n; ++j){
42             if(!vis[j] && dis[j] <= temp){
43                 temp = dis[j];
44                 flag = j;
45             }
46         }
47         vis[flag] = true;
48         ans += dis[flag];
49         for(j = 1; j <= n; ++j){
50             if(!vis[j] && map[flag][j] < dis[j])
51                 dis[j] = map[flag][j];
52         }
53     }
54     return ans;
55 }
56 
57 int main(){
58     int i,j;
59     double res,front,rear, mid;
60     while(EOF != scanf("%d",&n)){
61         if(n==0)    break;
62         for(i=1;i<=n;i++)
63             scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
64         front = 0;
65         rear = 100.0;//doubt
66         while(front <= rear){
67             mid = (front + rear) / 2;
68             creat(n,mid);
69             res = prim();
70             if(fabs(res) < 1e-4)
71                 break;
72             else if(res > 1e-4)
73                 front = mid;
74             else
75                 rear = mid;
76         }
77         printf("%.3f\n",mid);
78     }
79     return 0;
80 }

 

posted @ 2014-08-08 11:33  Jeremy Wu  阅读(507)  评论(0编辑  收藏  举报