记录想学过的并且想记的(其他网址都是收录的别人的博客)
有些经过正确数据测试的,有些没有,注意学习辨别
感觉这本书有很多错误
,所以都是看的其他博主的博客学习的
《计算机算法设计与分析》(王晓东 第5版)
二、递归与分治
整数划分
#include <iostream>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <algorithm>
#include <cstring>
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 <iostream>
#include <cstring>
#include <algorithm>
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 矩阵乘法
前提
C
=
A
∗
B
C = A * B
C=A∗B
通过上述博客的最终算数演化
M 1 = A 11 ∗ ( B 12 − B 22 ) M_1 = A_{11} * (B_{12} - B_{22}) M1=A11∗(B12−B22)
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∗(B21−B11)
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=(A12−A22)∗(B21+B22)
M 7 = ( A 11 − A 21 ) ∗ ( B 11 + B 12 ) M_7 = (A_{11} - A_{21}) * (B_{11} + B_{12}) M7=(A11−A21)∗(B11+B12)
C 11 = M 5 + M 4 − M 2 + M 6 C_{11} = M_5 + M_4 - M_2 + M_6 C11=M5+M4−M2+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+M1−M3−M7
棋盘覆盖
#include <iostream>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
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;
}
流水作业调度
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
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 <iostream>
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 <iostream>
#include <algorithm>
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 <iostream>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <string>
#include <queue>
#include <vector>
#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 <iostream>
#include <queue>
#include <algorithm>
#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 <iostream>
#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着色问题
#include <iostream>
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 <iostream>
#include <cstring>
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 <iostream>
#include <cstring>
#include <cmath>
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 <iostream>
#include <cstring>
#include <cmath>
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 <iostream>
#include <queue>
#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 <iostream>
#include <queue>
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
*/