《计算机算法设计与分析》

记录想学过的并且想记的(其他网址都是收录的别人的博客)
有些经过正确数据测试的,有些没有,注意学习辨别
感觉这本书有很多错误,所以都是看的其他博主的博客学习的

《计算机算法设计与分析》(王晓东 第5版)

  • 二、递归与分治
    • 整数划分
    • 汉诺塔
    • 大整数的乘法
    • Strassen 矩阵乘法
    • 棋盘覆盖
    • 线性时间选择算法
    • 最接近点对问题
      • 1.一维上最接近点对
      • 2.二维最接近点对
    • 循环赛日程表
  • 三、动态规划
    • 矩阵连乘次数最优解
    • 图像压缩
    • 电路布线
    • 流水作业调度
    • 最优二叉搜索树
    • 独立任务最优调度
  • 四、贪心
    • 哈夫曼编码
    • 多机调度问题
  • 五、回溯
    • 最大团问题
    • 图的m着色问题
    • 旅行售货员问题
    • 圆排列问题
    • 连续邮资问题
  • 六、分支限界算法
    • 装载问题
    • 装载问题优先队列

二、递归与分治

整数划分

整数划分

#include 
#include 
#include 

using namespace std;

int n, m;

int q(int n, int m)
{//此处可用记忆化搜索,也可用dp,已经实现并记录在别处,不重复写
    if (n < 1 || m < 1) return 0;
    if (n == 1 || m == 1) return 1;
    if (m > n) return q(n, n);
    if (n == m) return q(n, m - 1) + 1;
    return q(n, m - 1) + q(n - m, m);
}

int main()
{
    cin >> n;
    cout << q(n, n) << endl;
    return 0;
}

汉诺塔

汉诺塔

#include 
#include 
#include 

using namespace std;

void hanoi(int n, char a, char b, char c)
{
    if (n == 0) return ;
    hanoi(n - 1, a, c, b);//a借助c将n - 1个盘子移动到b
    cout << a << " --> " << c << endl;//将最大的盘子移动到c
    hanoi(n - 1, b, a, c);//b借助a盘将n - 1个盘子移动到c
}

int main()
{
    int n;
    cin >> n;
    hanoi(n, 'A', 'B', 'C');
    return 0;
}

大整数的乘法

大整数的乘法

#include 
#include 
#include 

using namespace std;

typedef long long LL;

int n;

LL qpow(LL a, int b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

int sign(LL x)
{
    if (x >= 0) return 1;
    return -1;
}

LL f(LL x, LL y, int n)
{
    int s = sign(x) * sign(y);
    x = x >= 0 ? x : -x;
    y = y >= 0 ? y : -y;

    if (n == 0)
        return 0;
    else if (n == 1)
        return s * x * y;
    else 
    {
        LL p = qpow(10, n / 2);
        LL a = x / p, b = x % p;
        LL c = y / p, d = y % p;
        LL ac = f(a, c, n / 2);
        LL bd = f(b, d, n / 2);
        LL abcd = f(a - b, d - c, n / 2) + ac + bd;
        return s * (ac * qpow(10, n / 2 + n / 2) + abcd * p + bd);
    }
}

int main()
{
    LL a, b;
    cin >> a >> b;
    int cnt = 0;
    LL c = max(abs(a), abs(b));
    while (c) cnt ++ , c /= 2;
    cout << f(a, b, cnt);
    return 0;
} 

Strassen 矩阵乘法

Strassen 矩阵乘法

前提 C = A ∗ B C = A * B C=AB
通过上述博客的最终算数演化

M 1 = A 11 ∗ ( B 12 − B 22 ) M_1 = A_{11} * (B_{12} - B_{22}) M1=A11(B12B22)
M 2 = ( A 11 + A 12 ) ∗ B 22 M_2 = (A_{11} + A_{12}) * B_{22} M2=(A11+A12)B22
M 3 = ( A 21 + A 22 ) ∗ B 11 M_3 = (A_{21} + A_{22}) * B_{11} M3=(A21+A22)B11
M 4 = A 22 ∗ ( B 21 − B 11 ) M4 = A_{22} * (B_{21} - B_{11}) M4=A22(B21B11)
M 5 = ( A 11 + A 22 ) ∗ ( B 11 + B 22 ) M5 = (A_{11} + A_{22}) * (B_{11} + B_{22}) M5=(A11+A22)(B11+B22)
M 6 = ( A 12 − A 22 ) ∗ ( B 21 + B 22 ) M_6 = (A_{12} - A_{22}) * (B_{21} + B_{22}) M6=(A12A22)(B21+B22)
M 7 = ( A 11 − A 21 ) ∗ ( B 11 + B 12 ) M_7 = (A_{11} - A_{21}) * (B_{11} + B_{12}) M7=(A11A21)(B11+B12)


C 11 = M 5 + M 4 − M 2 + M 6 C_{11} = M_5 + M_4 - M_2 + M_6 C11=M5+M4M2+M6
C 12 = M 1 + M 2 C_{12} = M_1 + M_2 C12=M1+M2
C 21 = M 3 + M 4 C_{21} = M_3 + M_4 C21=M3+M4
C 22 = M 5 + M 1 − M 3 − M 7 C_{22} = M_5 + M_1 - M_3 - M_7 C22=M5+M1M3M7

棋盘覆盖

棋盘覆盖

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1100;

int n, k, cnt;//k表示棋盘长度为2 ^ k
int a[N][N];

//x, y为特殊位置,sx为x轴起始位置,sy为y轴起始位置,k为次幂(坐标轴为计算机坐标轴)
void f(int sx, int sy, int x, int y, int n)
{
    if (n == 1) return ;
    int t = ++ cnt;
    int m = n / 2;

    //1
    if (x < sx + m && y < sy + m)
        f(sx, sy, x, y, m);
    else 
    {
        a[sx + m - 1][sy + m - 1] = t;
        f(sx, sy, sx + m - 1, sy + m - 1, m);
    }

    //2
    if (x < sx + m && y >= sy + m)
        f(sx, sy + m, x, y, m);
    else 
    {
        a[sx + m - 1][sy + m] = t;
        f(sx, sy + m, sx + m - 1, sy + m, m);
    }

    //3
    if (x >= sx + m && y < sy + m)
        f(sx + m, sy, x, y, m);
    else 
    {
        a[sx + m][sy + m - 1] = t;
        f(sx + m, sy, sx + m, sy + m - 1, m);
    }

    //4
    if (x >= sx + m && y >= sy + m)
        f(sx + m, sy + m, x, y, m);
    else 
    {
        a[sx + m][sy + m] = t;
        f(sx + m, sy + m, sx + m, sy + m, m);
    }
}

int main()
{
    int x, y; cin >> x >> y >> k;
    a[x][y] = ++ cnt;
    n = 1 << k;
    f(x, y, 0, 0, n);

    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < n; j ++ )
            printf("%4d", a[i][j]);
        cout << endl;
    }

    return 0;
}

线性时间选择算法

线性时间选择算法

#include 
#include 
#include 

using namespace std;

const int N = 1e5 + 9;

int n, a[N];

int Partition(int a[], int l, int r, int x)
{
    int id;
    for (int i = l; i <= r; i ++ )
        if (a[i] == x)
        {
            id = i;
            break;
        }

    swap(a[l], a[id]);

    int i = l, j = r + 1;
    while (i < j)
    {
        do i ++ ; while (a[i] < x);
        do j -- ; while (a[j] > x);
        if (i < j) swap(a[i], a[j]);
    }

    swap(a[l], a[j]);

    return j;
}

int select(int a[], int l, int r, int k)
{
    if (r - l + 1 <= 75)
    {
        sort(a + l, a + r + 1);
        return a[l + k - 1];
    }

    for (int i = 0; i < (r - l + 1) / 5; i ++ )
    {
        sort(a + l + 5 * i, a + l + 5 * i + 5);
        swap(a[l + i], a[l + 5 * i + 2]);
    }

    int mid = select(a, l, l + (r - l + 1) / 5 - 1, (r - l + 1) / 10);

    int i = Partition(a, l, r, mid);
    int j = i - l + 1;

    if (k <= j) return select(a, l, i, k);
    return select(a, i + 1, r, k - j);

}

int main()
{
    int k; cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    cout << select(a, 1, n, k) << endl;
    return 0;
}

最接近点对问题

最接近点对问题(包括一维二维)

1.一维上最接近点对

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

struct Node 
{
    double d;
    double d1, d2;
    bool operator <= (const Node& t)
    {
        return d <= t.d;
    }
};
int n;
double a[N];

double Max(double a[], int l, int r)
{
    double x = a[l];
    for (int i = l; i <= r; i ++ )
        x = max(x, a[i]);

    return x;
}

double Min(double a[], int l, int r)
{
    double x = a[l];
    for (int i = l; i <= r; i ++ )
        x = min(x, a[i]);
    
    return x;
}

int Partition(double a[], int l, int r, double x)
{
    int id = r + l >> 1;
    for (int i = l; i <= r; i ++ )
        if (fabs(a[i] - x) <= EPS)
        {
            id = i;
            break;
        }
    
    swap(a[l], a[id]);

    int i = l, j = r + 1;
    while (i < j)
    {
        do i ++ ; while (a[i] < x);
        do j -- ; while (a[j] > x);
        if (i <= j) swap(a[i], a[j]);
    }

    swap(a[l], a[j]);
    return j;
}

Node solve(double a[], int l, int r)
{
    Node res = {INF, 0, 0};
    if (l >= r) return res;
    double mx = Max(a, l, r), mn = Min(a, l, r);

    double mid_x = (mx + mn) / 2;
    int mid = Partition(a, l, r, mid_x);
    
    Node resl = solve(a, l, mid), resr = solve(a, mid + 1, r);

    res = res <= resl ? res : resl;
    res = res <= resr ? res : resr;

    double mxl = Max(a, l, mid), mnr = Min(a, mid + 1, r);
    resl = {abs(mxl - mnr), mxl, mnr};

    res = res <= resl ? res : resl;
    return res;
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    Node res = solve(a, 1, n);

    cout << res.d << " " << res.d1 << " " << res.d2 << endl;

    return 0;
}

最接近点对问题(包括一维二维)

2.二维最接近点对

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e3 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

struct PointX 
{
    int p;
    double x, y;
    bool operator <= (const PointX& t) 
    {
        return x <= t.x;
    }
};

struct PointY
{
    int p;
    double x, y;
    bool operator <= (const PointY& t)
    {
        return y <= t.y;
    }
};

//归并排序的合并数组操作,这个分治同样用到此合并函数,将a合并给b
template<typename T>
void merge(T a[], T b[], int l, int mid, int r)
{
    int i = l, j = mid + 1, k = l;
    while (i <= mid and j <= r)
    {
        if (a[i] <= a[j]) b[k ++ ] = a[i ++ ];
        else b[k ++ ] = a[j ++ ];
    }

    while (i <= mid) b[k ++ ] = a[i ++ ];
    while (j <= r) b[k ++ ] = a[j ++ ];
}

//将b数组区间l到r之间的数赋值给a
template<typename T>
void copy(T a[], T b[], int l, int r)
{
    for (int i = l; i <= r; i ++ )
        a[i] = b[i];
}

//归并排序
template<typename T>
void mergeSort(T a[], T b[], int l, int r)
{
    if (l >= r) return ;
    int mid = l + r >> 1;
    mergeSort(a, b, l, mid), mergeSort(a, b, mid + 1, r);
    merge(a, b, l, mid, r);
    copy(a, b, l, r);
}

//求两点距离函数
template<typename T>
double dis(T a, T b)
{
    double x = a.x - b.x;
    double y = a.y - b.y;
    return sqrt(x * x + y * y);
}
//将数组
void closest(PointX X[], PointY Y[], PointY Z[], int l, int r, PointX& a, PointX& b, double& d)
{
    if (r - l + 1 == 2)//两个点
    {
        a = X[l];
        b = X[r];
        d = dis(a, b);
        return ;
    }
    if (r - l + 1 == 3)
    {
        a = X[l];
        b = X[l + 1];
        d = dis(X[l], X[l + 1]);
        double d2 = dis(X[l], X[r]);
        double d3 = dis(X[l + 1], X[r]);
        if (d >= d2)
        {
            b = X[r];
            d = d2;
        }
        if (d >= d3)
        {
            a = X[l + 1], b = X[r];
            d = d3;
        }
        return ;
    }

    int mid = l + r >> 1;
    int f = l, g = mid + 1;

    //因为Y已经按照从小到大排好序了
    //所以按照编号分开之后两半也都是从小到大的
    for (int i = l; i <= r; i ++ )
    {
        if (Y[i].p > mid) Z[g ++ ] = Y[i];
        else Z[f ++ ] = Y[i];
    }

    PointX a1, b1;
    double d1;
    closest(X, Z, Y, l, mid, a, b, d); closest(X, Z, Y, mid + 1, r, a1, b1, d1);


    if (d1 < d)
    {
        d = d1;
        a = a1;
        b = b1;
    }

    merge(Y, Z, l, mid, r);//恢复Y数组

    //其中X数组已经按照x从小到大排好序
    //我们按照X的中线mid将两个数组分开,求完两边之后
    //我们找到距离小于d距离来进行划分
    int k = l;
    for (int i = l; i <= r; i ++ )
    {
        if (abs(X[mid].x - Y[i].x) < d)
            Z[k ++ ] = Y[i];
    }

    for (int i = l; i < k; i ++ )
    {//因为Y是按照y从小到大排好序的,所以遇到一个大于等于d的可以直接break
        for (int j = i + 1; j < k; j ++ )
        {
            if (Z[j].y - Z[i].y < d) break;
            double dd = dis(Z[i], Z[j]);
            if (dd < d)
            {
                d = dd;
                a = X[Z[i].p];
                b = X[Z[j].p];
            }
        }
    }

}

void solve(PointX X[], int n, PointX& a, PointX& b, double& d)
{
    if (n < 2) return ;
    PointX *tX = new PointX[n];
    mergeSort(X, tX, 0, n - 1);

    PointY *Y = new PointY[n];
    for (int i = 0; i < n; i ++ )
    {
        Y[i].p = X[i].p;
        Y[i].x = X[i].x;
        Y[i].y = X[i].y;
    }

    PointY *tY = new PointY[n];
    mergeSort(Y, tY, 0, n - 1);

    PointY *Z = new PointY[n];
    closest(X, Y, Z, 0, n - 1, a, b, d);

    delete[] tX;
    delete[] Y;
    delete[] tY;
    delete[] Z;

}

int main()
{
    int n;
    cin >> n;
    PointX *X = new PointX[n];
    for (int i = 0; i < n; i ++ )
    {
        double x, y;
        cin >> x >> y;
        X[i] = {i, x, y};
    }

    PointX a, b;
    double d;

    solve(X, n, a, b, d);

    cout << a.p << " " << a.x << " " << a.y << endl;
    cout << b.p << " " << b.x << " " << b.y << endl;

    cout << d << endl;

    return 0;
}

循环赛日程表

循环赛日程表

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e3 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

int a[N][N];

void dfs(int n, int k)
{
    if (n == 2)
    {
        a[k][1] = k;
        a[k][2] = k + 1;
        a[k + 1][1] = k + 1;
        a[k + 1][2] = k;
        return ;
    }

    dfs(n / 2, k);
    dfs(n / 2, k + n / 2);

    for (int i = k; i <= k + n / 2 - 1; i ++ )
        for (int j = 1; j <= n / 2; j ++ )
            a[i + n / 2][j + n / 2] = a[i][j];
    
    for (int i = k + n / 2; i <= k + n - 1; i ++ )
        for (int j = 1; j <= n / 2; j ++ )
            a[i - n / 2][j + n / 2] = a[i][j];
}

int main()
{

    int n;
    cin >> n;
    n = 1 << n;
    dfs(n, 1);
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
            cout << a[i][j] << " ";
        cout << endl;
    }
    return 0;
}

三、动态规划

矩阵连乘次数最优解

其实就是简单的区间dp

矩阵连乘次数最优解

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e3 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

int n;
int f[N][N];//表示从i到j的最优解,属性为次数最小值
int p[N];//设第Ai个矩阵的行为pi-1, 列为pi

//对于我来讲就是区间dp
int main()
{
    cin >> n;
    for (int i = 0; i <= n; i ++ ) cin >> p[i];

    //长度为1的结果为0
    memset(f, 0x3f, sizeof f);//先设置其他值为正无穷
    for (int i = 1; i <= n; i ++ ) f[i][i] = 0;

    for (int len = 2; len <= n; len ++ )
        for (int l = 1; l <= n - len + 1; l ++ )
        {
            int r = l + len - 1;
            for (int k = l; k < r; k ++ )
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + p[l - 1] * p[k] * p[r]);
        }

    cout << f[1][n] << endl;

    return 0;
}

图像压缩

图像压缩

#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

int n, f[N], a[N];
int l[N], b[N];
// int g[N][10];

int bit(int x)
{
    if (!x) return 1;
    int cnt = 0;
    while (x) cnt ++ , x /= 2;
    return cnt;
}

int bitmax(int l, int r)
{
    int res = a[l];
    for (int i = l; i <= r; i ++ ) 
        res = max(res, a[i]);
    
    //return bit(query(l, r)) * (r - l + 1);

    return bit(res) * (r - l + 1);
}

//因为O(长度小于8所以加上RMQ优化查询时间降低到O(1))
// void init()
// {
//     for (int j = 0; j <= 8; j ++ )
//         for (int i = 1; i + (1 << j) - 1 <= n; i ++ )
//             if (!j) g[i][j] = a[i];
//             else g[i][j] = max(g[i][j - 1], g[i + (1 << j - 1)][j - 1]);
// }

// int query(int l, int r)
// {
//     int len = r - l + 1;
//     int k = log(len) / log(2);
//     return max(g[l][k], g[r - (1 << k) + 1][k]);
// }

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    memset(f, 0x3f, sizeof f);
    f[0] = 0;

    for (int i = 1; i <= n; i ++ )
        for (int k = 1; k <= 256 && i >= k; k ++ )
        {
            int x = f[i - k] + bitmax(i - k + 1, i) + 11;
            if (x < f[i])
            {
                f[i] = x;
                l[i] = k;
                b[i] = bitmax(i - k + 1, i) / (k);
            }
        }

    cout << f[n] << endl;

    int t = n;
    vector<int> ve;

    while (t)
    {
        ve.push_back(l[t]);
        t -= l[t];
    }

    reverse(ve.begin(), ve.end());
    for (int i = 0, cnt = 0; i < ve.size(); i ++ )
    {
        cnt += ve[i];
        cout << l[cnt] << " " << b[cnt] << endl;
    }


    return 0;
}
/*
6
10 12 15 255 1 2

12
10 12 15  255 1 2 1 1  2 2 1 1
*/

电路布线

电路布线

#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e3 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

int n, a[N];
int f[N][N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    for (int j = 1; j <= n; j ++ )
        if (j < a[1]) f[1][j] = 0;
        else f[1][j] = 1;
    
    for (int i = 2; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            f[i][j] = f[i - 1][j];//相当于去掉i到a[i]这条线
            if (j >= a[i])//如果
                f[i][j] = max(f[i][j], f[i - 1][a[i] - 1] + 1);
        }

    cout << f[n][n] << endl;

    return 0;
}

流水作业调度

流水作业调度
Johnson法则证明

#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e3 + 9, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

struct Node
{
    int v, id;
    bool flag;
} e[N];

int a[N], b[N];
int res[N];

int main()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i] >> b[i];

    for (int i = 1; i <= n; i ++ )
    {
        e[i].v = a[i] <= b[i] ? a[i] : b[i];
        e[i].id = i;
        e[i].flag = a[i] <= b[i];
    }

    sort(e + 1, e + 1 + n, [&](Node& a, Node& b)
    {
        return a.v < b.v;
    });

    int l = 0, r = n + 1;
    for (int i = 1; i <= n; i ++ )
    {
        if (e[i].flag) res[ ++ l] = e[i].id;
        else res[ -- r] = e[i].id;
    }

    int res1 = a[res[1]], res2 = res1 + b[res[1]];
    for (int i = 2; i <= n; i ++ )
    {
        res1 += a[res[i]];
        res2 = max(res1 + res2) + a[res[i]];
    }

    cout << max(res1, res2) << endl;

    return 0;
}
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1500, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

struct Node
{
    int a, b, id;
} e[N];

int main()
{
    int n; cin >> n;
    // for (int i = 1; i <= n; i ++ )
    // {
    //     cin >> e[i].a >> e[i].b;
    //     e[i].id = i;
    // }

    for (int i = 1; i <= n; i ++ ) cin >> e[i].a;
    for (int i = 1; i <= n; i ++ ) cin >> e[i].b, e[i].id = i;

    sort(e + 1, e + 1 + n, [&](Node& x, Node& y)
    {
        return min(y.b, x.a) < min(y.a, x.b);
    });

    int res1 = 0, res2 = 0;
    for (int i = 1; i <= n; i ++ )
    {
        res1 += e[i].a;
        res2 = max(res1, res2) + e[i].b;
    }

    cout << res2 << endl;
    for (int i = 1; i <= n; i ++ )
        cout << e[i].id << " ";

    return 0;
}

最优二叉搜索树

最优二叉搜索树

#include 

using namespace std;

const int N = 1e2 + 9, INF = 0x3f3f3f3f;

int n;
double p[N], q[N];
int root[N][N];
double w[N][N], e[N][N];

void OBST()
{
    for (int i = 1; i <= n + 1; i ++ )
        w[i][i - 1] = e[i][i - 1] = q[i - 1];
    
    for (int len = 1; len <= n; len ++ )
        for (int l = 1; l <= n - len + 1; l ++ )
        {
            int r = l + len - 1;
            e[l][r] = INF;
            w[l][r] = w[l][r - 1] + p[r] + q[r];
            for (int k = l; k <= r; k ++ )
            {
                double t = e[l][k - 1] + e[k + 1][r] + w[l][r];
                if (t < e[l][r])
                {
                    e[l][r] = t;
                    root[l][r] = k;
                }
            }
        }

}

void build(int l, int r, int u)
{
    if (r < l - 1) return ;
    int p = root[l][r];

    if (r == l - 1)
    {
        if (r < u) cout << "d" << r << " 是 " << "k" << u << " 的左孩子" << endl;
        else cout << "d" << r << " 是 " << "k" << u << " 的右孩子" << endl;
        return ;
    }

    if (p < u) cout << "k" << p << " 是 " << "k" << u << " 的左孩子" << endl;
    else cout << "k" << p << " 是 " << "k" << u << " 的右孩子" << endl;

    build(l, p - 1, p);
    build(p + 1, r, p);
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> p[i];
    for (int i = 0; i <= n; i ++ ) cin >> q[i];

    OBST();
    build(1, n, -1);

    return 0;
}

/*
5
0.15 0.1 0.05 0.1 0.2
0.05 0.1 0.05 0.05 0.05 0.1
*/

独立任务最优调度

独立任务最优调度
O ( n m 2 ) O(nm ^ 2) O(nm2)的基础代码

#include 
#include 

using namespace std;

const int N = 1e2 + 9;

int n, m;
int a[N], b[N];
bool f[N][N][N];//表示第一台机器j时间完成作业前i个作业,第二台k时间完成前i个作业,

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i], m = max(a[i], m);
    for (int i = 1; i <= n; i ++ ) cin >> b[i], m = max(b[i], m);

    m *= n;//最大时间

    f[0][0][0] = true;
    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= m; k ++ )
            {
                if (j >= a[i]) f[i][j][k] = f[i - 1][j - a[i]][k];
                if (k >= b[i]) f[i][j][k] |= f[i - 1][j][k - b[i]];
            }

    int res = 1e9;
    for (int i = 0; i <= m; i ++ )
        for (int j = 0; j <= m; j ++ )
            if (f[n][i][j])
                res = min(res, max(i, j));
        
    cout << res << endl;


    return 0;
}
/*
6
2 5 7 10 5 2
3 8 4 11 3 4

答案:
15
*/

O ( n m ) O(nm) O(nm)优化代码

#include 
#include 
#include 

using namespace std;

const int N = 1e2 + 9;

int n, m;
int a[N], b[N];
int f[N][N];//设f[i][j]表示完成i个作业且机器A花费j时间的条件下机器B所花费时间的最小值

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i], m += a[i];
    for (int i = 1; i <= n; i ++ ) cin >> b[i];

    memset(f, 0x3f, sizeof f);
    for (int i = 0; i < a[1]; i ++ )
        f[1][i] = b[i];
    
    f[1][a[1]] = a[1] > b[1] ? b[1] : 0;

    for (int i = 2; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
        {
            f[i][j] = f[i - 1][j] + a[i];
            if (j >= a[i])
                f[i][j] = min(f[i][j], f[i - 1][j - a[i]]);
        }

    int res = 1e9;
    for (int j = 0; j <= m; j ++ )
        res = min(res, max(j, f[n][j]));

    cout << res << endl;

    return 0;
}
/*
6
2 5 7 10 5 2
3 8 4 11 3 4

答案:
15
*/

四、贪心

哈夫曼编码

哈夫曼编码

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
typedef pair<char, int> PCI;

struct Node
{
    int l, r;//左右节点
    int val, fa;//值和父亲节点
};
int n, m;//n为字符个数,m为2 * n - 1
unordered_map<char, int> cnt;//统计有多少个字符
unordered_map<char, string> ans;//统计每个字符的编码
unordered_map<int, Node> tree;//构建哈夫曼树
vector<PCI> ve;//统计多少个字符

void build()
{
    m = 2 * n - 1;

    priority_queue<PII, vector<PII>, greater<PII>> q;
    for (int i = 0; i < ve.size(); i ++ )
    {
        q.push({ve[i].y, i + 1});//第一个是出现的频率,第二个是编号
        tree[i + 1] = {0, 0, ve[i].y, 0};//构建哈夫曼树
    }

    for (int i = n + 1; i <= m; i ++ )
    {
        auto a = q.top(); q.pop();
        auto b = q.top(); q.pop();
        tree[a.y].fa = i;
        tree[b.y].fa = i;
        tree[i] = {a.y, b.y, a.x + b.x, i};
        q.push({a.x + b.x, i});
    }

}

void dfs(int u, string& s)
{
    if (u <= n)
    {
        ans[ve[u - 1].x] = s;//储存答案
        return ;
    }

    s.push_back('0');
    if (tree[u].l != 0) dfs(tree[u].l, s);
    s.pop_back();

    s.push_back('1');
    if (tree[u].r != 0) dfs(tree[u].r, s);
    s.pop_back();

}

int main()
{
    string s;
    cin >> s;
    n = s.size();

    for (char& c: s)
        cnt[c] ++ ;//查询每一个出现的次数

    if (cnt.size() == 1)
    {
        cout << "0" << endl;
        return 0;
    }

    for (auto& v: cnt)
        ve.push_back({v.x, v.y});//加入数组中

    n = cnt.size();

    build();
    
    string t = "";

    dfs(m, t);
    for (auto& v: cnt)
        cout << v.x << " " << ans[v.x] << endl;
    
    cout << "整个字符串编码为:" << endl;
    for (auto& c: s)
        cout << ans[c];

    return 0;
}


多机调度问题

多机调度问题

#include 
#include 
#include 

#define x first
#define y second

using namespace std;

const int N = 1e5 + 9, INF = 0x3f3f3f3f;

typedef pair<int, int> PII;

int n, m;
int a[N];

int solve()
{
    if (m >= n)
    {
        cout << "每个物品放一个机器" << endl;
        int res = a[1];
        for (int i = 1; i <= n; i ++ )
            res = max(res, a[i]);
        return res;
    }

    priority_queue<PII, vector<PII>, greater<PII>> q;
    for (int i = 1; i <= m; i ++ )
        q.push({a[i], i});
    
    //可以在循环过程中输出方案
    for (int i = m + 1; i <= n; i ++ )
    {
        PII t = q.top();
        q.pop();
        q.push({a[i] + t.x, i});
    }

    int res = 0;
    while (q.size())
    {
        res = max(q.top().x, res);
        q.pop();
    }
    return res;

}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    sort(a + 1, a + 1 + n, [&](int x, int y) {
        return x > y;
    });

    cout << solve() << endl;

    return 0;   
}

/*
7 3
2 14 4 16 6 5 3
*/

五、回溯

最大团问题

最大团问题

#include 

#define x first
#define y second

using namespace std;

const int N = 25, M = N << 1;

int n, ans;
int a[N][N];
bool st[N], res[N];

void dfs(int u, int cnt)
{
    if (u == n + 1)
    {
        ans = cnt;
        for (int i = 1; i <= n; i ++ )
            res[i] = st[i];
        return ;
    }

    st[u] = true;
    for (int i = 1; i < u; i ++ )
        if (st[i] && !a[u][i])
        {
            st[u] = false;
            break;
        }

    if (st[u]) dfs(u + 1, cnt + 1);
    st[u] = false;
    if (cnt + n - u > ans) dfs(u + 1, cnt);

}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            cin >> a[i][j];
    
    dfs(1, 0);

    cout << ans << endl;
    for (int i = 1; i <= n; i ++ )
        if (res[i])
            cout << i << " ";

    return 0;
}

/*
5
0 1 0 1 1
1 0 1 0 1
0 1 0 0 1
1 0 0 0 1
1 1 1 1 0
*/

图的m着色问题

图的m着色问题

#include 

using namespace std;

const int N = 25;

int n, m, ans;
int a[N][N];
int st[N];

bool check(int u)
{
    for (int i = 1; i <= n; i ++ )
        if (a[u][i] && st[u] == st[i]) 
            return false;
    return true;
}

void dfs(int u)
{
    if (u == n + 1)
    {
        ans ++ ;
        for (int i = 1; i <= n; i ++ )
            cout << st[i] << " ";
        puts("");
        return ;
    }

    for (int i = 1; i <= m; i ++ )
    {
        st[u] = i;
        if (check(u))
            dfs(u + 1);
        st[u] = 0;
    }
}

int main()
{
    cin >> n >> m;

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            cin >> a[i][j];
    
    dfs(1);
    
    cout << ans << endl;

    return 0;
}

/*
4 3
0 1 1 0
1 0 0 1 
1 0 0 1
0 1 1 0

5 4
0 1 1 1 0
1 0 1 1 1
1 1 0 1 0
1 1 1 0 1
0 1 0 1 0
*/

旅行售货员问题

旅行售货员问题

#include 
#include 

using namespace std;

const int N = 25, INF = 0x3f3f3f3f;

int n, m;//点数量,边数量
int a[N][N];
int ans, x[N], res[N];

void dfs(int i, int val)
{
    if (i == n)
    {
        if (a[x[n - 1]][x[n]] != INF && a[x[n]][x[1]] != INF && 
        val + a[x[n - 1]][x[n]] + a[x[n]][x[1]] < ans)
        {
            ans = val + a[x[n - 1]][x[n]] + a[x[n]][x[1]];
            for (int i = 1; i <= n; i ++ )
                res[i] = x[i];
        }
        return ;
    }

    for (int j = i; j <= n; j ++ )
    {
        if (a[x[i - 1]][x[j]] != INF && val + a[x[i - 1]][x[j]] < ans)
        {
            swap(x[i], x[j]);
            dfs(i + 1, val + a[x[i - 1]][x[j]]);
            swap(x[i], x[j]);
        }
    }

}

int main()
{
    cin >> n >> m;

    memset(a, 0x3f, sizeof a);

    for (int i = 1; i <= n; i ++ ) a[0][i] = 0;

    while (m -- )
    {
        int u, v, w;
        cin >> u >> v >> w;
        a[u][v] = a[v][u] = w;
    }
    
    for (int i = 1; i <= n; i ++ ) x[i] = i;

    ans = INF;
    dfs(1, 0);

    cout << ans << endl;
    for (int i = 1; i <= n; i ++ ) cout << res[i] << " ";

    return 0;
}

/*
4 6
1 2 30
1 3 6
1 4 4
2 3 5
2 4 10
3 4 20

*/

圆排列问题

圆排列问题

#include 
#include 
#include 

using namespace std;

const int N = 25, INF = 0x3f3f3f3f;

int n;
int x[N], res[N];
double r[N], ans = INF;

double f(int i, int j)
{
    if (!i) return r[j];
    double h = r[j] - r[i];
    double x = r[j] + r[i];
    return sqrt(x * x - h * h);
}

void dfs(int i, double val)
{
    if (i == n)
    {
        double t = f(x[n - 1], x[n]);
        if (t + val + r[x[n]] < ans)
        {
            ans = t + val + r[x[n]];
            for (int i = 1; i <= n; i ++ ) res[i] = x[i];
        }
        return ;
    }

    for (int j = i; j <= n; j ++ )
    {
        double t = f(x[i - 1], x[j]);
        if (val + t > ans) continue;

        swap(x[i], x[j]);
        dfs(i + 1, val + t);
        swap(x[i], x[j]);
    }
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> r[i];
    for (int i = 1; i <= n; i ++ ) x[i] = i;

    dfs(1, 0);

    cout << ans << endl;
    for (int i = 1; i <= n; i ++ ) 
        cout << res[i] << " ";

    return 0;
}

连续邮资问题

连续邮资问题

#include 
#include 
#include 

using namespace std;

const int N = 1e3 + 9, INF = 32767;

int n, m;
int x[N], y[N];
int res[N], ans;

void dfs(int i, int r)
{
    for (int j = 0; j <= x[i - 1] * m; j ++ )
        if (y[j] < m)
        {
            for (int k = 1; k <= m - y[j]; k ++ )
                if (y[j] + k < y[j + x[i] * k])
                    y[j + x[i] * k] = y[j] + k;
        }//更新上一层的y值

    while (y[r] < INF) r ++ ;//找到上一层的最大连续值

    if (i == n)
    {
        if (r - 1 > ans)
        {
            for (int i = 1; i <= n; i ++ )
                res[i] = x[i];
            ans = r - 1;
        }
        return ;
    }

    int z[N] = {0};
    for (int j = 0; j < N; j ++ )
        z[j] = y[j];
    
    for (int j = x[i] + 1; j <= r; j ++ )
    {//枚举当前层取什么值
        x[i + 1] = j;
        dfs(i + 1, r - 1);
        for (int k = 0; k < N; k ++ )
            y[k] = z[k];
    }

}

int main()
{
    cin >> n >> m;

    for (int i = 0; i < N; i ++ )
        y[i] = INF;

    x[1] = 1;
    y[0] = 0;
    
    ans = 0;
    dfs(1, 0);

    cout << ans << endl;

    for (int i = 1; i <= n; i ++ )  
        cout << res[i] << " ";

    return 0;
}

六、分支限界算法

装载问题

装载问题

#include 
#include 

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 25;

int n, m;
int w[N];

int bfs()
{
    int maxx = 0;
    for (int i = 1; i <= n; i ++ ) maxx += w[i];

    queue<PII> q;
    q.push({0, 0});//first 记录大小,second记录层数

    int res = 0;

    while (q.size())
    {
        PII t = q.front();
        q.pop();

        if (t.y == n) 
        {
            res = max(res, t.x);
            continue;
        }
        if (t.x + maxx - w[t.y + 1] >= res)
        {
            res = max(res, t.x);
            q.push({t.x, t.y + 1});
        }
        if (t.x + maxx >= res && t.x + w[t.y + 1] <= m) 
        {
            res = max(res, t.x + w[t.y + 1]);
            q.push({t.x + w[t.y + 1], t.y + 1});
        }

        if (q.size() && t.y != q.front().y) maxx -= w[t.y];

    }

    return res;

}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];

    int res = bfs();

    cout << res << endl;

    return 0;
}

/*
4 80
18 7 25 36
*/

装载问题优先队列

装载问题优先队列

#include 
#include 

using namespace std;

const int N = 25;

int n, m;
int w[N], s[N];

struct Node 
{
    int w, id, ww;
    friend bool operator < (const Node& a, const Node& b) 
    {
        return a.ww < b.ww;
    }
};

int bfs()
{
    priority_queue<Node, vector<Node>, less<Node>> q;

    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + w[i];

    q.push({0, 0, s[n]});

    int res = 0;

    while (q.size())
    {
        Node t = q.top();
        q.pop();

        if (t.id == n) return t.w;

        if (t.w + s[n] - s[t.id + 1] >= res)
        {
            res = max(res, t.w);
            q.push({t.w, t.id + 1, t.w + s[n] - s[t.id + 1]});
        }
        if (t.w + w[t.id + 1] <= m)
        {
            res = max(res, t.w + w[t.id + 1]);
            q.push({t.w + w[t.id + 1], t.id + 1, t.w + s[n] - s[t.id]});
        }

    }

    return res;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];

    cout << bfs() << endl; 
    return 0;
}
/*
4 80
18 7 25 36
*/

你可能感兴趣的:(C++,算法,c++)