跳转至

算法

回溯法(backtracking)

核心:构建决策树,有错误就回溯,注意剪枝(pruning)

  • 八皇后问题
  • 收费公路问题(turnpike reconstruction problem):一共有n(n-1)/2对距离。我们先通过最大距离确定两个端点的距离,然后再依次取剩下的最大的距离来确定新的点的位置(注意这个距离一定是与两个端点的,所以只有两种可能),再通过回溯得到最终结果

代码实现:

bool reconstruct(DistType x[],DistSet D,int N,int left,int right){
//D是给定的距离集合
//x[1]...x[left-1],x[right+1]...x[N]是已经确定位置的点
    bool Found=false;
    if(is_empty(D)) return True;
    D_max=Find_Max(D);
//case1:x[right]=D_max
    //check函数用来判断这个插入带来的新距离是否与D中距离冲突
    OK=Check(D_max,N,left,right);//pruning
    if(OK){
        x[right]=D_max;
    //更新D中的距离
        for(i=1;i<left;i++) delete(x[right]-x[i],D);
        for(i=right+1;i<=N;i++) delete(x[i]-x[right],D);
    //递归
        Found=reconstruct(x,D,N,left,right+1);
        if(!Found){
            //有冲突,回溯,恢复之前删掉的距离
            for(i=1;i<left;i++) insert(x[right]-x[i],D);
            for(i=right+1;i<=N;i++) insert(x[i]-x[right],D);
        }
    }
//case2:x[left]=x[N]-D_max
    if(!Found){
        OK=check(x[N]-D_max,N,left,right);
        if(OK){
            for(i=1;i<left;i++) delete(x[left]-x[i],D);
            for(i=right+1;i<=N;i++) delete(x[i]-x[left],D);
            Found=reconstruct(x,D,N,left+1,right);
            if(!Found){
            //有冲突,回溯,恢复之前删掉的距离
            for(i=1;i<left;i++) insert(x[left]-x[i],D);
            for(i=right+1;i<=N;i++) insert(x[i]-x[left],D);
            }
        }
    }
    return Found;
}
  • stick problem

示例代码

bool Backtracking(int i){
    bool Found=false;
    if(i>N) return True;
    for(each xi){
        OK=check(xi); //pruning
        if(OK){
            count xi in;
            Found=Backtracking(i+1);
            if(!Found){
                undo(i);
            }
        }
        if(Found) break;
    }
    return Found;
}

博弈

  • 最大最小搜索:当自己的回合时,找收益最大的,当对手回合时,找收益最小的
  • \(\alpha-\beta \ pruning\)

分治法

主定理: \(T(N)=aT(N/b)+O(N^klog^pN)\) \(\(T(N)=\begin{equation} \begin{cases} O(N^{log_ba}) & a>b^k \\ O(N^klog^{p+1}N) & a=b^k \\ O(N^klog^pN) & a<b^k \end{cases} \end{equation}\)\)

动态规划

近似算法

定义:对于算法A,如果对任意的示例(instance)I 都有:max{A(I)/opt(I),opt(I)/A(I)}<=p(I),则称A是一个p(n)-approximation algorithm

装箱问题(Bin packing)

  • next fit
  • 一个一个箱子装,如果一个箱子装不下新物体,就把这个箱子封起来然后新开一个箱子

!!! next fit的近似比为2.

证明:设算法用了k个箱子,每个箱子装了$S_k$个物体。则$S_{i}+S_{i-1} \geq 1$,则$opt=总物体数量\geq k-1/2$
  • first fit
  • 把物体装到第一个有空位的箱子里,如果没有再新开箱子
  • best fit
  • 把物体装到有空位且空余最小的箱子里(充分利用空间),如果没有再新开箱子
  • worst fit
  • 把物体装到有空位且空余最大的箱子里,如果没有再新开箱子

first fit和best fit 近似比为1.7;并且可以通过把输入按从大到小排序来优化。

背包问题 Knapsack problem

Fractional version

  • 每个物体都可以切割成一部分再放入背包
  • 算法:通过性价比(v/w)进行贪心算法

01背包

在01背包问题下,每个物体只有完全放入和不放入两种选择。

用动态规划算法可以解决:设f[i][j]表示前i个物体容量为j时的最大价值,显然对第i个物体,有放和不放两种选择:

f[ i ][ j ]=max{ f[ i - 1 ][ j ],f[ i - 1 ][ j - w[ i ] ]+ v[ i ] }

这个算法的时间复杂度是O(nV),V表示物品价值之和。但是这是一个伪多项式时间的,因为输入按照二进制表示后是指数级别的

多项式近似方案

A polynomial-time approximation scheme(PTAS) is a family of algorithm {\(A_k\)} such that for any k>0,\(A_k\) is a (1+k)-approximation algorithm that run in polynomial in n (given k is a constant).

也就是说,可以通过k来调节近似比,然后把k看成一个常数,此时得到的算法的时间复杂度是多项式时间的。

  • PTAS:\(O(n^{\frac{1}{k}})\)
  • efficient PTAS(EPTAS): \(O(f({\frac{1}{k}}) poly(n))\)
  • full PTAS(FPTAS): \(O(poly({\frac{1}{k}})poly(n))\)

k-center problem