模板题集合
本来存在电脑上的...怕有一天电脑突然炸了...所以博客上再备份一份
[队列]
priority_queue<int, vector<int>, greater<int> > q; // 从小到大
[最长公共子序列]
#include <iostream>
#include <cstring>
using namespace std;
int a[1010];
int b[1010];
int l[1010][1010];
int main() {
int n, m;
while (cin >> n >> m) {
for (int i = 1; i <= m; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &b[i]);
}
memset(l, 0, sizeof(l));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= m; ++j) {
if (a[i] == b[j]) {
l[i][j] = l[i-1][j-1] + 1;
} else {
l[i][j] = max(l[i-1][j], l[i][j-1]);
}
}
}
cout << l[m][m] << endl;
}
return 0;
}
[最长下降子序列]
#include <iostream>
using namespace std;
// 最长下降子序列
int arr[20010];
int m[20010];
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 0; i < n; ++i) {
scanf("%d", &arr[i]);
}
m[0] = 1;
int max_num = 1;
for (int i = 1; i < n; ++i) {
int tmp = 0;
for (int j = 0; j < i; ++j) {
if (arr[i] <= arr[j]) {
tmp = max(tmp, m[j]);
}
}
m[i] = tmp + 1;
max_num = max(max_num, m[i]);
}
cout << n - max_num << endl;
}
return 0;
}
[01背包裸题]
#include <iostream>
#include <cstring>
using namespace std;
int v[10010];
int w[10010];
int dp[10010];
int max(int a, int b) {
return a > b ? a : b;
}
int main() {
int bag, n;
while (cin >> bag >> n) {
for (int i = 1; i <= n; ++i) {
scanf("%d", &v[i]); //体积
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &w[i]); // 价值
}
memset(dp, 0, sizeof(dp)); // 必须要
for (int i = 1; i <= n; ++i) {
for (int j = bag; j >= v[i]; --j) {
dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
}
}
cout << dp[bag] << endl;
}
return 0;
}
[完全背包]
#include <iostream>
#include <cstring>
using namespace std;
int v[1010];
int w[1010];
int dp[10010];
int main() {
int n, bag;
while (cin >> n >> bag) {
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &w[i], &v[i]);
}
for (int i = 1; i <= bag; ++i) {
dp[i] = -2000000; // 负无穷,要求装满,否则等于0
}
for (int i = 1; i <= n; ++i) {
for (int j = v[i]; j <= bag; ++j) {
dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
}
}
cout << dp[bag] << endl;
}
return 0;
}
[多重背包]
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int v[2010];
int w[2010];
int num[1010];
int dp[20005];
int main() {
int t, n, bag;
cin >> t;
while (t--) {
cin >> bag >> n;
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &v[i], &w[i], &num[i]);
}
int k = n + 1;
for (int i = 1; i <= n; ++i) {
int d = 1;
while (num[i] > d) {
v[k] = d * v[i];
w[k++] = d * w[i];
num[i] -= d;
d *= 2;
}
v[i] *= num[i];
w[i] *= num[i];
}
for (int i = 1; i < k; ++i) {
for (int j = bag; j >= v[i]; --j) {
dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
}
}
cout << dp[bag] << endl;
}
return 0;
}
[混合背包]
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int v[2010];
int w[2010];
int num[1010];
int dp[20005];
int main() {
int n, bag;
while (cin >> bag >> n) {
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &v[i], &w[i], &num[i]);
}
int k = n + 1;
for (int i = 1; i <= n; ++i) {
if (num[i] <= 1) {
continue;
}
int d = 1;
while (num[i] > d) {
v[k] = d * v[i];
w[k++] = d * w[i];
num[i] -= d;
d *= 2;
}
v[i] *= num[i];
w[i] *= num[i];
}
for (int i = 1; i < k; ++i) {
if (num[i] > 0) // 01背包/多重背包
for (int j = bag; j >= v[i]; --j) {
dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
}
else // 完全背包
for (int j = v[i]; j <= bag; ++j) {
dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
}
}
cout << dp[bag] << endl;
}
return 0;
}
[区间dp]
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int arr[210], a;
long long int dp[210][210];
int sum[210];
int main(int argc, const char * argv[]) {
int n;
while (cin >> n) {
sum[0] = 0;
for (int i = 1; i <= n; ++i) {
cin >> a;
sum[i] = sum[i - 1] + a;
}
memset(dp, 0, sizeof(dp));
for (int len = 2; len <= n; ++len) {
for (int i = 1; i + len - 1 <= n; ++i) {
int j = i + len - 1;
for (int k = i; k < j; ++k) {
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i - 1]);
}
}
}
printf("%lld\n", dp[1][n]);
}
return 0;
}
[左右相关的区间dp]
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
int dp[110][110];
int main() {
string s;
while (cin >> s && s != "0") {
memset(dp, 0, sizeof(dp));
int max_size = (int)s.size();
for (int len = 2; len <= max_size; ++len) {
for (int i = 0; i < max_size; ++i) {
int j = i + len - 1;
if (j >= max_size) {
break;
}
for (int k = i; k < j; ++k) {
dp[i][j] = max(dp[i][j], dp[i][k]+dp[k+1][j]);
}
if ((s[i] == '(' && s[j] == ')')
|| (s[i] == '[' && s[j] == ']')) {
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
cout << dp[0][max_size - 1] << endl;
}
return 0;
}
[树状dp]
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
int arr[6010], par[6010];
int dp[6010][2];
vector<vector<int> > child;
int dfs(int now, int choice) {
if (dp[now][choice] != -1) {
return dp[now][choice];
}
// now 不选
if (choice == 0) {
dp[now][0] = 0;
for (int i = 0; i < child[now].size(); ++i) {
dp[now][0] += max(dfs(child[now][i], 0), dfs(child[now][i],1));
}
} else {
// now 选
dp[now][1] = arr[now];
for (int i = 0; i < child[now].size(); ++i) {
dp[now][1] += dfs(child[now][i], 0);
}
}
return dp[now][choice];
}
int main() {
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
}
int a, b;
child.clear();
child.resize(n + 1);
memset(par, -1, sizeof par);
for (int i = 0; i < n - 1; ++i) {
cin >> a >> b;
par[a] = b;
child[b].push_back(a);
}
cin >> a >> b;
// 记忆化搜索的初始化
memset(dp, -1, sizeof(dp));
// 边界
for (int i = 1; i <= n; ++i) {
if (child[i].size() == 0) {
// 叶子结点
dp[i][0] = 0, dp[i][1] = arr[i];
}
}
// 找根节点
int root = 0;
for (int i = 1; i <= n; ++i) {
if (par[i] == -1) {
root = i;
break;
}
}
// 搜索
cout << max(dfs(root, 0), dfs(root, 1)) << endl;
}
return 0;
}
[归并排序+求逆序对]
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
int data[10000010];
int tmp[10000010];
long long int cnt;
void Merge(int beg, int mid, int end) {
if (end <= beg)
return;
int i = 0;
int beg1 = beg, beg2 = mid;
while (beg1 < mid && beg2 < end) {
if (data[beg1] <= data[beg2]) {
tmp[i++] = data[beg1++];
} else {
tmp[i++] = data[beg2++];
cnt += mid - beg1;
}
}
while (beg1 < mid) {
tmp[i++] = data[beg1++];
}
while (beg2 < end) {
tmp[i++] = data[beg2++];
}
memcpy(data + beg, tmp, sizeof(int) * (end - beg));
}
int main(int argc, const char * argv[]) {
int n;
while (cin >> n) {
cnt = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &data[i]);
}
for (int length = 1; length <= n; length *= 2) {
int beg = 0, mid = beg + length, end = mid + length;
for(; beg < n; beg += 2 * length,
mid = beg + length, end = mid + length) {
if (mid > n) {
mid = n;
}
if (end > n) {
end = n;
}
Merge(beg, mid, end);
}
}
cout << cnt << endl;
}
return 0;
}
[最大连续子序列分治版]
#include <cstdio>
#include <iostream>
using namespace std;
int a[100010];
struct node {
int sum, left, right;
};
node recursionMax (int left, int right)
{
// 只有一个数
if (left == right) return {a[left], left, right};
int mid = (left + right) / 2;
node maxLeft = recursionMax (left, mid); // 左
node maxRight = recursionMax(mid + 1, right); // 右
int left_sum = -0x7fffffff, right_sum = -0x7fffffff, li = mid, ri = mid + 1;
int tmp = 0;
for (int i = mid; i >= left; i--)
{
tmp += a[i];
if (left_sum <= tmp) {
left_sum = tmp;
li = i;
}
}
tmp = 0;
for (int j = mid + 1; j <= right; j++)
{
tmp += a[j];
if (right_sum < tmp) {
right_sum = tmp;
ri = j;
}
}
node ret = maxLeft.sum >= maxRight.sum ? maxLeft : maxRight;
if (ret.sum > left_sum + right_sum) {
return ret;
} else if (ret.sum == left_sum + right_sum && ret.left < li) {
return ret;
} else {
return {left_sum + right_sum, li, ri};
}
}
int main ()
{
int n;
while (cin >> n) {
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
node ret = recursionMax(0, n - 1);
printf("%d %d %d\n", ret.sum, ret.left + 1, ret.right + 1);
}
return 0;
}
[点分治]
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
struct node {
double x, y;
} a[100010], b[100010];
bool cmpx(struct node i, struct node j) {
return i.x < j.x;
}
bool cmpy(struct node i, struct node j) {
return i.y < j.y;
}
double dis(struct node i, struct node j) {
return sqrt(pow(i.x - j.x, 2) + pow(i.y - j.y, 2));
}
double find_min(int left, int mid, int right, double s) {
int count = 0;
for (int i = left; i <= right; ++i) {
if (a[i].x >= a[mid].x - s && a[i].x <= a[mid].x + s) {
// 在中间
b[count].x = a[i].x;
b[count++].y = a[i].y;
}
}
sort(b, b + count, cmpy);
for (int i = 0; i < count; ++i) {
for (int j = i + 1; j < count; ++j) {
if (b[j].y - b[i].y > s) {
break;
}
double ret = dis(b[i], b[j]);
if (ret < s) {
s = ret;
}
}
}
return s;
}
double recursionMax (int left, int right) {
if (left >= right) {
return 0x7fffffff;
}
// 只有两个数
if (left == right - 1) {
return dis(a[left], a[right]);
}
int mid = (left + right) / 2;
double x;
if (right - left == 2) {
x = dis(a[left], a[right]);
x = min(x, dis(a[mid], a[right]));
x = min(x, dis(a[mid], a[left]));
return x;
}
double minLeft = recursionMax (left, mid); // 左
double minRight = recursionMax(mid, right); // 右
double s = minLeft <= minRight ? minLeft : minRight;
s = find_min(left, mid, right, s);
return s;
}
int main ()
{
int n;
while (cin >> n && n) {
for (int i = 0; i < n; ++i) {
cin >> a[i].x >> a[i].y;
}
sort(a, a + n, cmpx);
double ret = recursionMax(0, n - 1);
printf("%0.2lf\n", ret / 2);
}
return 0;
}
[atoi]
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int n;
char *str = "12345.67";
n = atoi(str);
printf("n=%d\n",n);
return 0;
}
输出:
n = 12345
[itoa](若不能用,可用sprintf)
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int number = 123456;
char string[25];
itoa(number, string, 10);
printf("integer=%d string=%s\n”, number, string);
return 0;
}
[sprintf]
#include <stdio.h> //头文件
int main(void)
{
int number = 123456;
char string[25]; // 必须为 char* 类型
// 参数:数组,以何种形式输出,数字
sprintf(string, "%d", number);
printf("integer=%d string=%s\n”, number, string);
return 0;
}
[gcd 最大公约数]
long long int gcd(long long int a, long long int b) {
return (b == 0) ? a : gcd(b, a % b);
}
[lcm 最小公倍数]
long long int lcm(long long int a, long long int b) {
return a * b / gcd(a, b);
}
[大数相加]
参数:
数1:a ,长度:len1
数2:b ,长度:len2
注:
a[0] 也存储数,a 从下标为 0 处开始存储(b相同),且 a 和 b 为顺序存储,即:
数字:123
数组 a:a[0] = 1, a[1] = 2, a[3] = 3;
[code]
void sum_a_b(char a[], char b[], int len1, int len2) {
int q = 0;
len1--, len2--; // len1 和 len2 为长度,减 1 后为下标
int sum[1010], i = 0; // sum 数组存储结果,i 存储 sum 的下标
while (len1 >= 0 && len2 >= 0) { // 从两数组最后一位开始计算
sum[i++] = (q + a[len1] + b[len2] - 2 * '0') % 10;
q = (q + a[len1--] + b[len2--] - 2 * '0') / 10;
}
// 若某一个数较长,继续加
while (len1 >= 0) {
sum[i++] = (q + a[len1] - '0') % 10;
q = (q + a[len1--] - '0') / 10;
}
while (len2 >= 0) {
sum[i++] = (q + b[len2] - '0') % 10;
q = (q + b[len2--] - '0') / 10;
}
// 加上进位的 q,若 q 为0 也没关系,下一步会去除 0
sum[i] = q;
// 若得出的 sum 中开头有过多的0
while (sum[i] == 0) {
--i;
}
// 这里为输出,需要将 sum 从后往前输出
for (int j = i; j >= 0; --j) {
cout << sum[j];
}
cout << endl;
}
[数论]
数根:各位数字之和
x 的树根 = (x - 1) % 9 + 1
[图论]
若图中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。
若该路径是一个圈,则称为欧拉(Euler)回路。
[图的存储]
定理:
(一)一个图有“欧拉回路”当且仅当它是连通的且每个顶点都有偶数度。
(二)一个图有“欧拉通路”当且经当它是连通的且除两个顶点外,其他顶点都有偶数度。(含奇数度的两个节点中,一个必为欧拉通路起点,另一个必为欧拉通路的终点)
[连通分量]:无向图中的极大连通子图
[拓扑排序]
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
struct node {
int _to; // 若有权,加一个 int l;
int _next;
} edge[550];
int num;
int in[550]; // 入度
int ret[550];
int head[550];
void insert(int x, int y) {
edge[num]._to = y;
edge[num]._next = head[x];
head[x] = num++;
}
int main(int argc, const char * argv[]) {
int n, m;
while (cin >> n >> m) {
num = 0;
memset(head, -1, sizeof(head));
int x, y;
for (int i = 0; i < m; ++i) {
scanf("%d%d", &x, &y);
insert(x, y);
++in[y];
}
priority_queue<int, vector<int>, greater<int> > q;
for (int i = 1; i <= n; ++i) {
if (in[i] == 0) q.push(i);
}
int k = 0;
while (!q.empty()) {
int a = q.top();
ret[k++] = a;
q.pop();
for (int now = head[a]; now != -1; now = edge[now]._next) {
--in[edge[now]._to];
if (in[edge[now]._to] == 0) {
q.push(edge[now]._to);
}
}
}
for (int i = 0; i < n - 1; ++i) {
cout << ret[i] << ' ';
}
cout << ret[n - 1] << endl;
}
return 0;
}
关键路径:从源点到汇点的最长路径长度
dijkstral算法 (复杂度O(n^2))——不能有负权边
spfa算法 (复杂度O(km))——不能有负权环
Floyd (复杂度O(n^3))——可以求多源点最短路
[dijkstra]
#include <iostream>
#include <cstring>
#include <stack>
#include <cstdio>
#include <vector>
using namespace std;
const int inf = 0x3f3f3f3f;
int n, m;
int road[1010][1010];
int vis[1010], dis[1010], pre[1010];
bool dijkstra(int s, int e) {
vis[s] = 1;
for (int i = 1; i < n; ++i) {
int minD = inf, k = -1;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[j] < minD) {
minD = dis[j];
k = j;
}
}
vis[k] = 1;
if (k == e) break;
if (k == -1) return 0; // 不通
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[k] + road[k][j] < dis[j]) {
dis[j] = dis[k] + road[k][j];
pre[j] = k;
}
}
}
return 1;
}
int main() {
while (cin >> n >> m) {
int a, b, z;
for (int i = 1; i <= n; ++i) {
fill(road[i] + 1, road[i] + n + 1, inf);
}
for (int i = 0; i < m; ++i) {
cin >> a >> b >> z;
road[a][b] = min(z, road[a][b]); // 有向
road[b][a] = min(z, road[b][a]); // 无向
}
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i) {
dis[i] = road[1][i];
pre[i] = 1;
}
dis[1] = 0;
if (dijkstra(1, n) == 0) {
cout << -1 << endl;
continue;
}
int k = pre[n];
stack<int> s;
while (k != 1) {
s.push(k);
k = pre[k];
}
cout << 1 << ' ';
while (!s.empty()) {
cout << s.top() << ' ';
s.pop();
}
cout << n << endl;
}
return 0;
}
[FLOYD算法]
for (int i = 1; i <= 32; ++i) {
for (int j = 1; j <= 32; ++j) {
for (int k = 1; k <= 32; ++k) {
if (i != j && j != k && k != i
&& dis[i][k] + dis[k][j] <= dis[i][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
}
最小生成树:生成子图是一棵树,且树枝的权值在所有生成树中,总和最小。
prim 算法 (复杂度O(n^2))——适用于稠密图
kruskal算法 (复杂度O(m*lg(m))——适用于稀疏图
/////////////////////////////
对于Prim,其选取的边(u,v)必有一个顶点已经被覆盖,另一个顶点未被覆盖。
Kruskal算法:先将所有的边做排序,然后利用并查集作判断来优先选择较小的边,直到建成一棵生成树。对于Kruskal,其选取的边(u,v)任意,只要这个边的加入不能使被覆盖的顶点构成回路。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
struct node {
int x, y, l; // 起点 重点 长度
} edge[1010];
int a[1010], b[1010], fa[1010];
bool cmp(node x, node y) {
return x.l < y.l;
}
int findfa(int x) {
return fa[x] == x ? x : (fa[x] = findfa(fa[x]));
}
void merge(int i, int j) {
fa[findfa(i)] = findfa(j);
}
long long int kruskal(int num) {
int cnt = 0;
long long int sum = 0;
for (int i = 0; i < num; ++i) {
int fx = findfa(edge[i].x);
int fy = findfa(edge[i].y);
if (fx != fy) {
merge(fx, fy);
++cnt;
sum += edge[i].l;
if (cnt >= num - 1) {
break;
}
}
}
return sum;
}
int main() {
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
fa[i] = i;
}
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i];
}
int num = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (i != j) {
edge[num].x = i;
edge[num].y = j;
edge[num++].l = a[i] * b[j] + a[j] * b[i];
}
}
}
sort(edge, edge + num, cmp);
cout << kruskal(num) << endl;
}
return 0;
}
哈哈哈哈哈哈