题目链接: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;
}