zoukankan      html  css  js  c++  java
  • 01分数规划小结

    基础知识

    邻接矩阵与链式前向星

    邻接矩阵:适用于要快速修改以及点密集的图

    链式前向星:适用于需要快速查询与之相连点,且点稀疏的图。

    最小生成树

    prim加点法

    正确性

    prim将最小生成树分为两个部分,因为关键在于点,一个部分为已知的最小生成树构造,另外一个部分为未知的生成树,而加上两个部分最小的连边,同样能使两棵树联通,必然选择最短的边最好。

    代码实现:

    int dis[1001][1001];
    int prim(){
      int i,j,k,mst(0);
      bool check[1001],mini[1001];
      memset(mini,1,sizeof(mini)),mini[1]=0;
      for(i=1;i<=n;++i){
        for(k=0,j=1;j<=n;++j)
            if(mini[k]>mini[j]&&!check[j])k=j;
        check[k]|=true,mst+=mini[k];
        for(j=1;j<=n;++j)
          if(dis[k][j]<mini[j]&&!is[j])
            mini[j]=dis[k][j];
      }
      return mst;
    }
    
    

    kruscal排序

    正确性

    kruscal将边从小到大排序,即变成n颗树,而最小生成树必然是由这些树所构成的,而对于最小生成树包含的两棵生成树,将两者联通,必然是选择所有未选的边可以将其联通的最小的边。

    代码实现

    struct edge{
    int p1,p2,len;
    il bool operator<(edge&x){
        return len<x.len;
    }
    }e[1001];
    struct union_find{
        int father[2001];
        il void clear(int n){
            for(int i(1);i<=n;++i)father[i]=i;
        }
        il int find(int x){
            if(father[x]==x)return x;
            return father[x]=find(father[x]);
        }
        il void link(int x,int y){
            father[find(x)]=find(y);
        }
    }u;int n,m;
    il int kruscal(){
        int i,mst(0);
        sort(e+1,e+m+1),u.clear(n);
        for(i=1;i<=m;++i)
            if(u.find(e[i].p1)!=u.find(e[i].p2))
                u.link(e[i].p1,e[i].p2),mst+=e[i].len;
        return mst;
    }
    

    联系

    思想

    1. 红蓝点
    2. 数学归纳集合关系(不看单个点关系,看树与树之间关系)
    3. 排序优化(最短最少一般可以排序)
    4. 反证法给予证明

    适用范围

    算法 范围
    prim 点少边多
    kruscal 点多边少

    spfa判负环

    前身

    bellmanford

    思想

    蔓延(不断地更新必然能最小),而负环使之无限更新。

    代码

    bfs

    struct point{
        point*next;int to,len;
    }*head[1001],*pt;
    bool exist[1001];
    int n,dis[1001],num[1001];
    il bool check(){
        memset(dis,0,sizeof(dis)),memset(num,0,sizeof(num)),
            memset(exist,0,sizeof(exist));int i;
        for(i=1;i<=n;++i)if(spfa(i))return true;
        return false;
    }
    il bool spfa(int s){
        queue<int>team;int i;
        team.push(s),exist[s]|=true;
        while(!team.empty()){
            s=team.front(),team.pop(),exist[s]&=false;
            for(pt=head[s];pt!=NULL;pt=pt->next)
                if(dis[pt->to]>dis[s]+pt->len){
                    dis[pt->to]=dis[s]+pt->len;
                    if(!exist[pt->to]){
                        team.push(pt->to);
                        if(++num[pt->to]>n)return true;
                    }
                }
        }return false;
    }
    
    

    dfs

    struct point{
        point*next;int to,len;
    }*head[1001],*pt;
    bool exist[1001];
    int n,dis[1001];
    il bool check(){
        memset(dis,0,sizeof(dis)),memset(exist,0,sizeof(exist));
        for(int i(1);i<=n;++i)if(spfa(i))return true;
        return false;
    }
    il bool spfa(int x){
        exist[x]|=true;
        for(point *i(head[x]);i!=NULL;i=i->next)
            if(dis[i->to]>dis[x]+i->len){
                dis[i->to]=dis[x]+i->len;
                if(exist[i->to]||spfa(i->to))
                    return true;
            }
        return exist[x]&=false;
    }
    

    01分数规划

    问题

    给定({a_i}{b_i}),求({x_i}(x_i=0 or 1))使(frac{sum_{i=1}^na_ix_i}{sum_{i=1}^nb_ix_i})最大,
    感性的理解,即有n对a,b,从中选出x对,使a之和除以b之和最大。

    最大问题即最优化问题,目前算法已经有

    1. 递推
    2. 贪心(常用排序)
    3. 搜索
    4. 二分
    5. 迭代

    而式子的问题常与二分扯上关系,于是我们来倒腾一下式子,设

    [s=frac{sum_{i=1}^na_ix_i}{sum_{i=1}^nb_ix_i} ]

    [sum_{i=1}^nsb_ix_i=sum_{i=1}^na_ix_i ]

    注意到相同的x,于是考虑合并

    [sum_{i=1}^nx_i(a_i-b_is)=0 ]

    不妨将这个式子叫做分数规划的二分式,注意到sigma最左边为一常数,于是考虑预处理,接着考虑如何在这个式子中取到最大,显然是选择所有的整数,此时如果

    [sum_{i=1}^nx_i(a_i-b_is)>0 ]

    [sum_{i=1}^nsb_ix_i<sum_{i=1}^na_ix_i ]

    [s<frac{sum_{i=1}^na_ix_i}{sum_{i=1}^nb_ix_i} ]

    于是在我们所变出的式子中找到的最大值大于0的话,意思即存在更大的解可以满足条件,于是我们可以进行二分,这是一种方法。

    但是注意到我们所选出来的式子也是一组比较大的解,于是可以让之作为端点,再以之为基础继续寻找最优解,无限接近答案,也就是迭代,这是另一种方法。

    于是我们现在来考虑它们的区别

    算法 优点 缺点
    二分 只需要判断是否有更优解 漫无目的地去寻找答案
    迭代 接近答案比二分会更加快 需要额外花时间算出解

    于是我们可以得出结论,如果答案好算,当然迭代会快一些,否则二分会更好,两者互补,共同组成了分数规划这一知识点,接下来所有题目也是围绕他们俩展开的。

    基本模型

    最优比率生成树

    问题

    给出一张图,边权为({a_i,b_i}),选出一棵生成树,使a之和除以b最小。

    方法

    1. 写出分数式

    [sum_{i=1}^nx_i(a_i-b_is)=0 ]

    1. 对括号里的数作为边权跑最小生成树,以此作为判断依据

    最优比率生成环

    两个概念

    环:起点和终点都为自己,且只有一条路径。

    联通分量(环套环):其中任意一个点都能到达其他所有点的路径。

    意义:我们能够解决环的问题,而问题涉及联通分量,我们需要想办法转换。

    问题

    给出一张图,边权为({a_i,b_i}),选出个强联通分量,使a之和除以b最小。

    方法

    1. 写出分数式

    [sum_{i=1}^nx_i(a_i-b_is)=0 ]

    1. 对括号里的数作为边权跑spfa负环,以此作为判断依据

    分数结论

    分数三角不等式结论

    (a_1,b_1,a_2,b_2in Z^*,frac{a_1}{b_1}geqfrac{a_2}{b_2}Rightarrow frac{a_2}{b_2}leqfrac{a_1+a_2}{b_1+b_2}leq frac{a_1}{b_1})

    证明:

    [frac{a_1+a_2}{b_1+b_2}-frac{a_1}{b_1}=frac{a_2b_1-a_1b_2}{(b_1+b_2)b_1}geq0 ]

    同理,另一边也可以用同样的方式证明出来。

    套路总结

    基本

    1. 二分式正负随意,注意变换使之顺应问题。
    2. 要想分数规划一定要a,b对应

    证明小结

    1. 常用反证法
    2. 比大小可以用作差法
  • 相关阅读:
    GSS3 SPOJ 1716. Can you answer these queries III gss1的变形
    GSS1 spoj 1043 Can you answer these queries I 最大子段和
    Codeforces Round #197 (Div. 2) C,D两题
    sgu 185 最短路建网络流
    CF 208E
    QTREE2 spoj 913. Query on a tree II 经典的倍增思想
    BZOJ 1146: [CTSC2008]网络管理Network 树链剖分+线段树+平衡树
    ubuntu安装vim
    历史背景更新模型
    码本模型
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10802542.html
Copyright © 2011-2022 走看看