题目链接:Click here~~
题意:
没看,直接搜的别人的建图。
解题思路:
调了一上午,哎。下面转自大神博客。
概念
有带权图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.
二分法。(1200MS)
#include <queue> #include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N = 1e3 + 5; double d[N],w[N][N]; bool vis[N]; double prim(int n) { memset(vis,false,sizeof(vis)); memset(d,127,sizeof(d)); int u = 1 , times = 0; double ans = 0; while(++times < n) { vis[u] = true; for(int v=1;v<=n;v++) if(!vis[v] && w[u][v] < d[v]) d[v] = w[u][v]; double mmin = d[0]; for(int v=1;v<=n;v++) if(!vis[v] && mmin > d[v]) mmin = d[v] , u = v; if(mmin == d[0]) return -1; ans += mmin; } return ans; } struct Point { int x,y,z; void read(){ scanf("%d%d%d",&x,&y,&z); } int sqr(int x){ return x * x; } double dis(const Point& P){ return sqrt( sqr(x-P.x)*1.0 + sqr(y-P.y) ); } int cost(const Point& P){ return abs(z-P.z); } }A[N]; double dis[N][N]; int cost[N][N]; void build(int n,double L) { for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) w[i][j] = w[j][i] = cost[i][j] - L*dis[i][j]; } const double eps = 1e-5; int main() { int n; while(scanf("%d",&n),n) { for(int i=1;i<=n;i++) A[i].read(); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { dis[i][j] = dis[j][i] = A[i].dis(A[j]); cost[i][j] = cost[j][i] = A[i].cost(A[j]); } double l = 0 , r = 100; while(r-l > eps) { double mid = (l+r) / 2; build(n,mid); if(prim(n) > eps) l = mid; else r = mid; } printf("%.3f\n",r); } return 0; }
迭代法。(250MS)
#include <queue> #include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N = 1e3 + 5; double dis[N][N]; int cost[N][N]; double d[N],w[N][N]; bool vis[N]; int pre[N]; double prim(int n) { if(n == 1) return 0; memset(vis,false,sizeof(vis)); memset(d,127,sizeof(d)); int u = 1 , times = 0; double dis = 0; int cost = 0; while(++times < n) { vis[u] = true; for(int v=1;v<=n;v++) if(!vis[v] && w[u][v] < d[v]) d[v] = w[u][v] , pre[v] = u; double mmin = d[0]; for(int v=1;v<=n;v++) if(!vis[v] && mmin > d[v]) mmin = d[v] , u = v; if(mmin == d[0]) return -1; cost += ::cost[pre[u]][u]; dis += ::dis[pre[u]][u]; } return cost / dis; } struct Point { int x,y,z; void read(){ scanf("%d%d%d",&x,&y,&z); } int sqr(int x){ return x * x; } double dis(const Point& P){ return sqrt( sqr(x-P.x)*1.0 + sqr(y-P.y) ); } int cost(const Point& P){ return abs(z-P.z); } }A[N]; void build(int n,double L) { for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) w[i][j] = w[j][i] = cost[i][j] - L*dis[i][j]; } const double eps = 1e-5; int main() { int n; while(scanf("%d",&n),n) { for(int i=1;i<=n;i++) A[i].read(); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { dis[i][j] = dis[j][i] = A[i].dis(A[j]); cost[i][j] = cost[j][i] = A[i].cost(A[j]); } double last = -1 , cur = 0; while(fabs(cur-last) > eps) { last = cur; build(n,last); cur = prim(n); } printf("%.3f\n",cur); } return 0; }