1. 程式人生 > >01分數規劃學習筆記

01分數規劃學習筆記

IV 最優 大小 break math tour 坐標 fine and

01分數規劃學習筆記

今天 gsh 帶著我們復習了一下01分數規劃。

01分數規劃就是假設一個物體有兩個屬性 a,b,同時選擇在這個集合中的k個物品,使:

\[ v=\dfrac{\sum^{i=1} _{i<=n} a_i \cdot x_i}{\sum ^{i=1} _{i<=n} b_i \cdot x_i} ,\sum_{i=1}^{n}x_i=k \]
的值最大。

那麽如何能做到呢?看這個式子我們很難看出如何求解,那麽將其變形。
\[ v \cdot \sum^{i=1} _{i<=n} b_i \cdot x_i- \sum^{i=1}_{i<=n} a_i\cdot x_i=0 \]


提取公因式
\[ \sum ^{i=1}_{i<=n} x_i \cdot(v\cdot b_i-a_i)=0 \]
那麽,我們可以二分一個v,設一個變量
\[ c_i=(v\cdot b_i-a_i) \]
sort後選擇最大的k個,二分如下。

    while(r-l>1e-5) {
            double mid=(l+r)/2;
            if(check(mid))
                l=mid;
            else r=mid;
        }

然後就有最優比率生成環(用spfa等等判正環),最優比率生成樹(最大生成樹)求出sumc是否大於0即可。

附一道題,poj2728,Desert King,最優比率生成樹。

題目大意:有n個村莊,每個村莊有一個高度和坐標,要求建一個生成樹,每條邊有兩個屬性是距離與代價,要求單位長度的花費最小 (代價的大小就是這兩個村莊的高度差的絕對值)。

//Writer : Hsz %WJMZBMR%tourist%hzwer
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<stack> #include<vector> #include<cstdlib> #include<algorithm> #define LL long long using namespace std; int n; const int N=1004; int x[N],y[N],z[N]; bool vis[N]; double a[N][N],b[N][N],c[N][N],dis[N];//鄰接矩陣存圖。 double dist(int d,int e) { return (x[d]-x[e])*(x[d]-x[e])+(y[d]-y[e])*(y[d]-y[e]); } bool check(double v) { memset(vis,0,sizeof vis); vis[1]=1; for(int i=1; i<=n; i++) dis[i]=b[1][i]-v*a[1][i]; double ans=0; for(int i=2; i<=n; i++) {//prim求最小生成樹 double tp=1061109567; int k; for(int j=2; j<=n; j++) if(!vis[j] and dis[j]<tp) k=j,tp=dis[j]; if(k==-1) break; vis[k]=1; ans+=tp; for(int j=2; j<=n; j++) { if(!vis[j] and b[k][j]-v*(a[k][j])<dis[j]) { dis[j]=b[k][j]-v*(a[k][j]); } } } return ans>=0; } int main() { while(~scanf("%d",&n)&&n) { for(int i=1; i<=n; i++) { scanf("%d%d%d",&x[i],&y[i],&z[i]); } for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(i!=j) a[i][j]=sqrt(dist(i,j)),b[i][j]=abs(z[i]-z[j]); double l=0,r=1e9; while(r-l>1e-5) {//二分 double mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%.3lf\n",l); } return 0; }

01分數規劃學習筆記