• 【算法】01分数规划 --- HNOI2009最小圈 & APIO2017商旅 & SDOI2017新生舞会


      01分数规划:通常的问法是:在一张有 (n) 个点,(m) 条边的有向图中,每一条边均有其价值 (v) 与其代价 (w);求在图中的一个环使得这个环上所有的路径的权值和与代价和的比率最小大。即求 (frac{sum v}{sum w}) 的最小值最大值。

      通常的解法也是比较固定的,我们首先假设求最大值,最优的答案为 (L),(L = frac{sum v}{sum w})。接下来我们对于这个式子进行变形:

    (L * sum w = sum v)

    (L * sum w - sum v = 0)

      注意到这里面的 (L) 是我们假设出来的最优解。若我们二分这个答案,设我们当前二分出来的答案为 (R),则考虑当 (R < L) 和 (R > L) 的时候会分别出现什么状况。当我们选定了一个 (R),一条边的 (R * w - v) 就是一个固定的值,不妨将它视作新的边权。而原式便是这些边权的和。若此时图中有负环,因为 (R * w - v) 随 (R) 的增大单调不减,说明可以将 (R) 的值设定得更大,有更有的解。存在零环说明此时的 (R) 恰好等于 (L),没有负环 & 零环说明不能取到当前值。

     

    1.HNOI2009最小圈

      裸的01分数规划,只是最大变成了最小。做法还是一样,将spfa转化为最长路判正环即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 10050
    #define eps 0.0000000001
    #define db double
    int n, m, cnp = 1, head[maxn];
    db ans, dis[maxn];
    bool vis[maxn], mark[maxn];
    
    struct edge
    {
        int to, last;
        db co, w;
    }E[maxn];
    
    void add(int u, int v, db w)
    {
        E[cnp].to = v, E[cnp].w = w;
        E[cnp].last = head[u], head[u] = cnp ++;
    }
    
    bool spfa(int u)
    {
        vis[u] = 1, mark[u] = 1;
        for(int i = head[u]; i; i = E[i].last)
        {
            int v = E[i].to;
            if(dis[v] < dis[u] + E[i].co) 
            {
                dis[v] = dis[u] + E[i].co;
                if(vis[v] || spfa(v)) { vis[u] = 0; return 1; }
            }
        }
        vis[u] = 0;
        return 0;
    }
    
    bool check()
    {
        memset(mark, 0, sizeof(mark));
        memset(dis, 0, sizeof(dis));
        for(int i = 1; i <= n; i ++)
            if(!mark[i] && spfa(i)) return 1;
        return 0;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i ++)
        {
            int u, v; db w;
            scanf("%d%d%lf", &u, &v, &w);
            add(u, v, w);
        }
        db l = -100000, r = 100000;
        while(l + eps < r)
        {
            db mid = (l + r) / 2.0;
            for(int i = 1; i < cnp; i ++) E[i].co = mid - E[i].w;
            if(check()) ans = mid, r = mid;
            else l = mid;
        }
        printf("%.8lf
    ", ans);
        return 0;
    } 

    2.APIO2017商旅

      其实也是裸裸的一道题,建图的方式略有隐藏罢了。注意到两个点之间只能携带一种物品,我们将这些路径建成边连起来,然后套路即可。只不过因为这题我二分的是整数,所以要注意判零环。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 105
    #define INF 99999999
    int n, m, T, a[maxn][maxn * 10][2];
    int R[maxn][maxn], val[maxn][maxn];
    int ans, E[maxn][maxn], dis[maxn];
    bool mark[maxn], vis[maxn];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    void Floyd()
    {
        for(int k = 1; k <= n; k ++)
            for(int i = 1; i <= n; i ++)
                for(int j = 1; j <= n; j ++)    
                    R[i][j] = min(R[i][j], R[i][k] + R[k][j]);
    }
    
    void Build()
    {
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
            {
                if(R[i][j] >= INF) continue;
                for(int k = 1; k <= T; k ++)
                    if(~a[i][k][0] && ~a[j][k][1]) 
                    {
                        int v = a[j][k][1] - a[i][k][0];
                        if(v > val[i][j]) val[i][j] = v;
                    }
            }
    }
    
    bool spfa(int u)
    {
        vis[u] = 1, mark[u] = 1;
        for(int i = 1; i <= n; i ++)
        {
            if(i == u || R[i][u] >= INF) continue;
            if(dis[i] > dis[u] + E[i][u] || (!mark[i] && dis[i] == dis[u] + E[i][u]))
            {
                dis[i] = dis[u] + E[i][u];
                if(vis[i] || spfa(i)) { vis[u] = 0; return 1;}
            }
            else if(dis[i] == dis[u] + E[i][u] && vis[i])
            { vis[u] = 0; return 1; }
        }
        vis[u] = 0;
        return 0;
    }
    
    bool check()
    {
        memset(dis, 0, sizeof(dis));
        memset(mark, 0, sizeof(mark));
        for(int i = 1; i <= n; i ++)
            if(!mark[i] && spfa(i)) return 1;
        return 0;
    }
    
    void work()
    {
        int l = 1, r = 5000;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            for(int i = 1; i <= n; i ++)
                for(int j = 1; j <= n; j ++)
                {
                    if(R[i][j] >= INF) continue;
                    E[i][j] = mid * R[i][j] - val[i][j];
                }
            if(check()) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
    }
    
    int main()
    {
        n = read(), m = read(), T = read();
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= 2 * T; j ++)
                if(j & 1) a[i][(j >> 1) + 1][0] = read();
                else a[i][j >> 1][1] = read();
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                if(i != j) R[i][j] = INF; 
        for(int i = 1; i <= m; i ++)
        {
            int x = read(), y = read(), t = read();
            R[x][y] = min(R[x][y], t);
        }
        Floyd();
        Build();
        work(); 
        printf("%d
    ", ans);
        return 0; 
    } 

     3.SDOI2017新生舞会

      就套路吧……只不过要注意下精度。【我的代码跑得非常非常的慢……】

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 450
    #define INF 9999999999.9
    #define int long long
    #define db double
    #define eps 0.00000001
    
    int n, a[maxn][maxn], b[maxn][maxn];
    int flow[maxn], S, T, pre[maxn];
    int cnp, head[maxn];
    deque <int> q; bool vis[maxn], flag;
    db dis[maxn], cost;
    
    struct edge
    {
        int to, last, u, f, F;
        db co;
    }E[maxn * 400];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    void add(int u, int v, int f, int co)
    {
        E[cnp].u = u, E[cnp].to = v, E[cnp].co = (db) co; 
        E[cnp].last = head[u]; E[cnp].f = E[cnp].F = f; head[u] = cnp ++;
        E[cnp].u = v, E[cnp].to = u, E[cnp].co = -(db) co; 
        E[cnp].last = head[v]; E[cnp].f = E[cnp].F = 0; head[v] = cnp ++;
    }
    
    void init()
    {
        int a = 2 * n + 1;
        for(int i = 0; i <= a; i ++) dis[i] = INF;  
    }
    
    bool SPFA()
    {
        q.push_back(S); init();
        flow[S] = INF, vis[S] = 1, dis[S] = 0;
        while(!q.empty())
        {
            int u = q.front(); q.pop_front(); vis[u] = 0;
            for(int i = head[u]; ~i; i = E[i].last)
            {
                int v = E[i].to;
                if(E[i].f && dis[v] > dis[u] + E[i].co)
                {
                    dis[v] = dis[u] + E[i].co; pre[v] = i;
                    flow[v] = min(flow[u], E[i].f);
                    if(!vis[v])
                    {
                        vis[v] = 1;
                        if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);
                        else q.push_back(v);
                    }
                }
            }
        }
        if(dis[T] >= INF) return 0; else return 1; 
    }
    
    db work()
    {
        cost = 0; int ans = 0;
        while(SPFA())
        {
            int e = pre[T];
            while(2333)
            {
                E[e].f -= flow[T], E[e ^ 1].f += flow[T];
                if(E[e].u != S) e = pre[E[e].u];
                else break;
            }
            ans += flow[T]; cost += (db) flow[T] * dis[T];
        }
        return cost;
    }
    
    signed main()
    {
        n = read(); T = 2 * n + 1;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                a[i][j] = read();
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                b[i][j] = read();
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                add(i, j + n, 1, 0);
        for(int i = 1; i <= n; i ++) 
        {
            add(i + n, T, 1, 0);
            add(S, i, 1, 0); 
        }
        db r = 10000.0, l = eps, ans = 0;
        while(r - l > eps)
        {
            db mid = (r + l) / 2.0;
            for(int i = 0; i < cnp; i += 2)
            {
                int u = E[i].u, v = E[i].to; E[i].f = E[i].F, E[i ^ 1].f = E[i ^ 1].F;
                if(!u || !v || u == 2 * n + 1 || v == 2 * n + 1) continue;
                E[i].co = -((db) a[u][v - n] - mid * (db) b[u][v - n]);
                E[i ^ 1].co = - E[i].co; 
            }
            db mf = work();
            if(mf > 0) r = mid;
            else ans = mid, l = mid;
        }
        printf("%.6lf
    ", ans);
        return 0;
    }
  • 相关阅读:
    AdminLTE 3.0发布了
    .NET Core ORM 类库Petapoco中对分页Page添加Order By对查询的影响
    【译】ASP.NET Core在 .NET Core 3.1 Preview 1中的更新
    物品编码中心所说的包装指示符
    Entity Framework Core生成的存储过程在MySQL中需要进行处理及PMC中的常用命令
    Asp.Net Core Mvc Razor之RazorPage
    基于Asp.Net Core MVC和AdminLTE的响应式管理后台之侧边栏处理
    Qt的下载地址
    由于MicrosoftVisualStudio14.0DesignerShadowCache导致的一个异常问题
    Spring系列.事务管理原理简析
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9106319.html
Copyright © 2020-2023  润新知