• ACM-ICPC (10/12)


    01分数规划

    背景:根据楼教主回忆,曾经在一场比赛中秒掉了一道最优比例生成树问题,导致很多人跟风失败,最终悲剧。

    • 什么是01分数规划呢?

    这样的等式求最大,最小即为01分数规划。  

    如果你不知道该如何去解,你可能会去贪心,DP去做,但是这样是很复杂的。

    • 解法:二分,迭代(计算几何大佬都知道这种方案,但是我不是)

    • 直接二分ans, 根据符号二分转移。

    例题一:pku 2796

     

    题意: 最大。

    #include <stdio.h>
    #include <algorithm>using namespace std;
    ​
    const int maxn = 1005;
    ​
    int n,k;
    double a[maxn],b[maxn];
    double c[maxn];
    ​
    bool cmp(double a,double b) {
        return a > b;
    }
    ​
    bool calc(double x) {
        for(int i = 0; i < n; i++)
            c[i] = a[i] - x*b[i];
        sort(c,c+n,cmp);
    ​
        double sum = 0;
        for(int i = 0; i < n-k; i++)
            sum +=c[i];
        if(sum>=0) return true;
        return false;
    ​
    }
    ​
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(scanf("%d%d",&n,&k),n) {
    ​
            for(int i = 0; i < n; i++) scanf("%lf",&a[i]);
            for(int i = 0; i < n; i++) scanf("%lf",&b[i]);
    ​
            double l = 0,r = 1;
    ​
            while(r-l>1e-6) {
                double mid = (l + r)/2;
    ​
                if(calc(mid))
                    l = mid;
                else r = mid;
    ​
            }
            printf("%.0lf
    ",l*100);
    ​
        }
        return 0;
    }

    例题二:pku 2728 最优比例生成树

    题意:给定n 个点,坐标(x,y,z),n条无向边的图,国王将这n个点连起来(生成树),建一条边有花费, 求单位最小花费最小比例。

    同理:二分这个比例,边权为 ,最小生成树 ans >= 0,说明 x过小,二分转移 l = mid;

    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <iostream>
    #include <algorithm>using namespace std;
    ​
    const int maxn = 1005;
    ​
    double maps[maxn][maxn];
    bool vis[maxn];
    double dis[maxn];
    ​
    int n;
    ​
    double Prim() {
        memset(vis,false,sizeof(vis));
        for(int i = 1; i<= n; i++)
            dis[i] = 1000000000;
    ​
        double ans = 0;
        dis[1] = 0;
    ​
        for(int i = 1; i <= n; i++) {
            double tmp = 1000000000;
            int k = 0;
    ​
            for(int j = 1; j <= n; j++) {
                if(!vis[j]&&dis[j]<tmp) {
                    tmp = dis[j];
                    k = j;
                }
            }
    ​
            vis[k] = true;
            ans += tmp;
    ​
            for(int j = 1; j<= n; j++) {
                if(!vis[j]&&dis[j]>maps[k][j])
                    dis[j] = maps[k][j];
            }
    ​
        }
        return ans;
    }
    ​
    struct Node {
        double x,y,z;
    }nodes[maxn];
    ​
    double dist(int i,int j,double x) {
        double fx = fabs(nodes[i].x-nodes[j].x);
        double fy = fabs(nodes[i].y-nodes[j].y);
        double fz = fabs(nodes[i].z-nodes[j].z);
        return fz - x*sqrt(fx*fx+fy*fy);
    }
    ​
    double eps = 1e-4;
    ​
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(scanf("%d",&n),n) {
    ​
            for(int i = 1; i <= n; i++) scanf("%lf%lf%lf",&nodes[i].x,&nodes[i].y,&nodes[i].z);
    ​
            double l = 0,r = 1000000000;
    ​
            while(r-l>1e-5) {
                double mid = (r+l)/2;
    ​
                for(int i = 1; i <= n; i++)
                    for(int j = 1; j <= n; j++)
                        maps[i][j] = dist(i,j,mid);
    ​
                double ans = Prim();
    ​
                if(ans<=0)
                    r = mid;
                else l = mid;
            }
    ​
            printf("%.3f
    ",l);
        }
    ​
        return 0;
    }

    例题三:pku 3621 最优比例环。(双倍经验题Uva 11090,题意相反)

    题意:给定一个L个节点,P条有向边的图,奶牛从一个城市出发,走一个环回到起点,点上有权值,边上也有长度,求单位长度的点权最大。

    分析:还是二分 ans,由于是一个环,一条边上,算起点权值就好了。改边权,

    由于求的是比例最大,这时SPFA,应反向松弛,才能得到最大的比例。

    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <string.h>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #include <iostream>
    #include <algorithm>using namespace std;
    ​
    const int maxn = 1005;
    ​
    struct Edge {
        int from,to;
        double dist;
    };
    ​
    struct BellmanFord
    {
        int n, m;
        vector<Edge> edges;
        vector<int> G[maxn];
        bool inq[maxn];
        double d[maxn];
        int p[maxn];
        int cnt[maxn];
    ​
        void init(int n)
        {
            this->n = n;
            for(int i = 0; i < n; i++) G[i].clear();
            edges.clear();
        }
    ​
        void AddEdge(int from, int to, double dist)
        {
            edges.push_back((Edge)
            {
                from, to, dist
            });
            m = edges.size();
            G[from].push_back(m-1);
        }
    ​
        bool negativeCycle()
        {
            queue<int> Q;
            memset(inq, 0, sizeof(inq));
            memset(cnt, 0, sizeof(cnt));
            for(int i = 0; i < n; i++)
            {
                d[i] = 0;
                inq[0] = true;
                Q.push(i);
            }
    ​
            while(!Q.empty())
            {
                int u = Q.front();
                Q.pop();
                inq[u] = false;
                for(int i = 0; i < (int)G[u].size(); i++)
                {
                    Edge& e = edges[G[u][i]];
                    if(d[e.to] < d[u] + e.dist) //反向松弛
                    {
                        d[e.to] = d[u] + e.dist;
                        p[e.to] = G[u][i];
                        if(!inq[e.to])
                        {
                            Q.push(e.to);
                            inq[e.to] = true;
                            if(++cnt[e.to] > n) return true;
                        }
                    }
                }
            }
            return false;
        }
    }sol;
    int L,P;
    double f[maxn];
    double t[maxn];
    ​
    vector<Edge> edgestmp;
    ​
    int main()
    {
        //freopen("in.txt","r",stdin);
        scanf("%d%d",&L,&P);
    ​
        sol.init(L);
        for(int i = 0; i < L; i++) scanf("%lf",&f[i]);
        for(int i = 0; i < P; i++) {
            int u,v;
            double dist;
            scanf("%d%d%lf",&u,&v,&dist);
            u--;v--;
            edgestmp.push_back((Edge){u,v,dist});
            sol.AddEdge(u,v,dist);
        }
    ​
        double l = 0,r = 10000;
        while(r-l>1e-4) {
            double mid = (r+l)/2;
    ​
            sol.init(L);
    ​
            for(int i = 0; i < P; i++) {
                int u = edgestmp[i].from;
                int v = edgestmp[i].to;
                double dist = edgestmp[i].dist;
                sol.AddEdge(u,v,f[u]-mid*dist);
            }
    ​
            if(sol.negativeCycle())
                l = mid;
            else r = mid;
        }
    ​
        printf("%.2f
    ",l);
        return 0;
    }


  • 相关阅读:
    js:数据结构笔记13--检索算法
    ember.js:使用笔记9 开始单元测试
    js:数据结构笔记12--排序算法(2)
    js:数据结构笔记11--排序算法(1)
    js:数据结构笔记10--图和图算法
    js:数据结构笔记9--二叉树
    js:数据结构笔记8--集合
    js:数据结构笔记7--哈希表
    js:数据结构笔记6--字典
    js:数据结构笔记5--链表
  • 原文地址:https://www.cnblogs.com/TreeDream/p/7658441.html
Copyright © 2020-2023  润新知