整理的算法模板合集: ACM模板
目录
-
- A、杰杰国王的平等节(easy)【签到】
- B、最佳复习效果(easy - mid)【二分查找】
- C、无限之路(mid - hard)【离散化+前缀和】
- D、Is acm here?(easy)【签到】
- E、填数游戏(easy)【规律】
- F、阿伟的店铺(easy - mid)【中位数】
- G、魔法阵(mid - hard)【线性DP】
- H、公益活动(mid)【简单DFS】
- I、选拔人才(mid - hard)【贪心+优先队列 / 暴力贪心】
- J、2048(mid - hard)【大模拟】
- K、Alice和Bob的爱恨情仇(mid - hard)【构造】
- L、石同学的树(hard)【树hash】
- M、简单数论(hard)【欧拉函数+快速幂+拓展欧几里得+因数分解+解同余方程(doge)】
比赛地址:https://ac.nowcoder.com/acm/contest/9681#question
邀请码:swpu2020
大家可以点开上面的比赛地址进入比赛页面提交代码补题。
A、杰杰国王的平等节(easy)【签到】
签到题,只需要找到序列最大值计算即可。
#include <iostream>
using namespace std;
int a[1000005];
int main() {
int n, m = 0, sum = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
m = max(m, a[i]);
}
for (int i = 1; i <= n; i++) {
sum += m - a[i];
}
printf("%d", sum);
return 0;
}
B、最佳复习效果(easy - mid)【二分查找】
数据为 1 0 5 10^5 105, O ( n 2 ) O(n^2) O(n2)乱搞是过不去的(其实数据还是有点弱)
我们可以使用二分查找 O ( n l o g n ) O(nlogn) O(nlogn) 稳定通过本题。
#include <stdio.h>
#define max(x,y) (x>y?x:y)
const int maxn = 1e5 + 5;
int n, k, a[maxn], ans;
void find(int l, int r, int val, int &pos) {
if (l >= r)
return;
int m = (l + r) / 2;
if (a[m] <= val)
find(m + 1, r, val, pos);
else {
pos = m;
find(l, m, val, pos);
}
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
for (int i = 0; i < n; i++) {
int x = a[i], lim = k - a[i], pos = -1;
find(0, i, lim, pos);
if (pos == -1)
pos = i;
if (pos - 1 < 0)
continue;
int y = a[pos - 1];
ans = max(ans, x * y);
}
printf("%d", ans);
return 0;
}
开一个桶暴力搞(数据较弱)
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int a[10005];
int main() {
int n, k;
scanf("%d %d", &n, &k);
int Max = 0;
for (int i = 1; i <= n; i++) {
int digit;
scanf("%d", &digit);
a[digit]++;
}
for (int i = 1; i <= 10000; i++)
for (int j = 1; j <= 10000; j++) {
if (i == j) {
if (a[i] >= 2) {
if (i + j <= k)
Max = max(Max, i * j);
}
} else {
if (a[i] >= 1 && a[j] >= 1) {
if (i + j <= k)
Max = max(Max, i * j);
}
}
}
printf("%d", Max);
return 0;
}
C、无限之路(mid - hard)【离散化+前缀和】
离散化+前缀和模板题
我们发现数轴的数据达到了 1 0 18 10^{18} 1018,查询的数据达到了 1 0 5 10^5 105,所以我们查询的时候需要用前缀和 O ( 1 ) O(1) O(1)查询,由于数轴过长,我们不可能开一个 1 0 18 10^{18} 1018的数组存下整个数轴,所以我们需要用到一个离散化的思想(具体百度)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
typedef int ll;
const int N = 500007;
using namespace std;
ll n, m;
ll a[N];
vector<ll>b;
ll sum[N];
ll X[N], C[N], L[N], R[N];
ll tot = 0;
ll Find(ll x)
{
ll res = lower_bound(b.begin(), b.end(), x) - b.begin();
return res;
}
int main()
{
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
scanf("%d%d", &n, &m);
b.push_back(-1e18);
for(int i = 1; i <= n; ++ i){
scanf("%d%d", &X[i], &C[i]);
b.push_back(X[i]);
}
for(int i = 1; i <= m; ++ i){
scanf("%d%d", &L[i], &R[i]);
b.push_back(L[i]);
b.push_back(R[i]);
}
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for(int i = 1; i <= n; ++ i){
sum[Find(X[i])] += C[i];
}
int num = b.size() - 1;
for(int i = 1; i <= num; ++ i)
sum[i] += sum[i - 1];
for(int i = 1; i <= m; ++ i){
ll ans = sum[Find(R[i])] - sum[Find(L[i]) - 1];
printf("%d\n", ans);
}
return 0;
}
D、Is acm here?(easy)【签到】
签到题
#include <bits/stdc++.h>
#define reg register
#define ios ios::sync_with_stdio(false)
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int eps = 1e-10;
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
int main()
{
int t;
cin>>t;
while(t--){
string s;
cin>>s;
for(auto &it : s) it = tolower(it);
int cnt = 0;
for(int i = 0;i < s.size();++i){
if(s[i] == 'a') cnt == 0 && cnt++;
if(s[i] == 'c') cnt == 1 && cnt++;
if(s[i] == 'm') cnt == 2 && cnt++;
}
cnt == 3 ? puts("YES") : puts("NO");
}
return 0;
}
E、填数游戏(easy)【规律】
找规律
#include<stdio.h>
int main(){
int n;
scanf("%d", &n);
printf("%d", n + 1);
}
F、阿伟的店铺(easy - mid)【中位数】
答案就是中位数。
由于数据达到了 1 0 5 10^5 105,我们排序的时候需要使用快速排序 O ( n l o g n ) O(nlogn) O(nlogn),或者直接用C++自带的sort函数排序。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn = 1e5 + 10;
ll a[Maxn];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
a[i] = i;
sort(a + 1, a + n + 1);
ll ans = 0;
int pos = n + 1 >> 1;
for (int i = 1; i <= n; i++) {
ans += abs(a[i] - a[pos]);
}
printf("%lld", ans);
return 0;
}
G、魔法阵(mid - hard)【线性DP】
非常抱歉,本题的题目描述有些不清楚,赛前验题的时候没有发现,导致全场只有这一题没有人AC。(话说你们发现题目有问题的话可以提问呀)
其实本题就是一个非常简单的线性DP,我们需要放完所有的魔法石,但是魔法阵可以不放完,所以我们只需要考虑对于每个魔法阵来说是放置魔法石还是不放置魔法石为好,以及注意初始化的细节即可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1200;
int f[N][N], w[N][N];
int main() {
int F, V;
scanf("%d%d", &F, &V);
for (int i = 1; i <= F; i++)
for (int j = 1; j <= V; j++)
cin >> w[j][i];
memset(f, 0xcf, sizeof(f));
for (int i = 1; i <= V; i++)
f[i][0] = 0;
f[1][1] = w[1][1];
for (int i = 2; i <= V; i++)
for (int j = 1; j <= F; j++)
f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + w[i][j]);
printf("%d\n", f[V][F]);
return 0;
}
H、公益活动(mid)【简单DFS】
数据只有10,直接爆搜即可。结果没什么人做…
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int a[N];
int group[N][N];
int ans = INF;
bool vis[N];
bool check(int g[], int gc, int x){
for(int i = 0;i < gc; ++ i){
if(__gcd(a[g[i]], a[x]) > 1)
return false;
}
return true;
}
void dfs(int gnum, int gc, int putnum, int now){
bool flag = true;
if(gnum >= ans)return ;
if(putnum == n)ans = gnum;
for(int i = now; i < n;++ i){
//可以放下
if(!vis[i] && check(group[gnum], gc, i)){
vis[i] = true;
group[gnum][gc] = i;
dfs(gnum, gc + 1, putnum + 1, i + 1);
vis[i] = false;
flag = false;
}
}
//放不了开新组
if(flag)dfs(gnum + 1, 0, putnum, 0);
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n;++ i){
scanf("%d", &a[i]);
}
dfs(1, 0, 0, 0);
printf("%d\n", ans);
return 0;
}
I、选拔人才(mid - hard)【贪心+优先队列 / 暴力贪心】
数据较弱,可以不适用优先队列直接暴力贪心也是可以通过的。
我们按照代码可读性从大到小排序,因为代码可读性是一个下限,我们只需要在满足代码可读性的下限的前提下找最大的可能答案即可,使用优先队列寻找最大值,更新最优解即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> p;
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, k;
scanf("%d%d", &n, &k);
vector<p> a;
for (int i = 0, b, c; i < n; ++i)
{
scanf("%d%d", &b, &c);
a.push_back(make_pair(c, b));
}
sort(a.begin(), a.end());
reverse(a.begin(), a.end());
priority_queue<int, vector<int>, greater<int> > q;
ll sum = 0;
ll ans = 0;
for (int i = 0; i < a.size(); ++i)
{
if (q.size() < k)
{
q.push(a[i].second);
sum += a[i].second;
}
else if (a[i].second > q.top())
{
sum += a[i].second;
sum -= q.top();
q.pop();
q.push(a[i].second);
}
ans = max(ans, sum * a[i].first);
}
cout << ans << endl;
}
return 0;
}
J、2048(mid - hard)【大模拟】
直接按照题意模拟即可。
#include <bits/stdc++.h>
#define reg register
#define ios ios::sync_with_stdio(false)
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int eps = 1e-10;
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
struct node{
int g[6][6];
void movel()
{
for(int i = 1;i <= 5;++i){
for(int j = 2;j <= 5;++j){
int pos = j;
while(pos > 1 && (g[i][pos-1] == 0 || g[i][pos-1] == g[i][pos])){
g[i][pos-1] += g[i][pos];
g[i][pos] = 0;
pos--;
}
}
}
}
void mover()
{
for(int i = 1;i <= 5;++i){
for(int j = 4;j >= 1;--j){
int pos = j;
while(pos < 5 && (g[i][pos+1] == 0 || g[i][pos+1] == g[i][pos])){
g[i][pos+1] += g[i][pos];
g[i][pos] = 0;
pos++;
}
}
}
}
void moveu()
{
for(int j = 1;j <= 5;++j){
for(int i = 2;i <= 5;++i){
int pos = i;
while(pos > 1 && (g[pos - 1][j] == 0 || g[pos - 1][j] == g[pos][j])){
g[pos - 1][j] += g[pos][j];
g[pos][j] = 0;
pos--;
}
}
}
}
void moved()
{
for(int j = 1;j <= 5;++j){
for(int i = 4;i >= 1;--i){
int pos = i;
while(pos < 5 && (g[pos + 1][j] == 0 || g[pos + 1][j] == g[pos][j])){
g[pos + 1][j] += g[pos][j];
g[pos][j] = 0;
pos++;
}
}
}
}
void print()
{
for(int i = 1;i <= 5;++i){
for(int j = 1;j <= 4;++j)printf("%d ",g[i][j]);
printf("%d\n",g[i][5]);
}
}
}mat;
int main()
{
#ifndef ONLINE_JUDGE
freopen("4.in","r",stdin);
freopen("4.out","w",stdout);
#endif
int t;
cin>>t;
while(t--){
string op;
cin>>op;
if(op == "Left"){
mat.movel();
}
if(op == "Right"){
mat.mover();
}
if(op == "Up"){
mat.moveu();
}
if(op == "Down"){
mat.moved();
}
int x,y;
cin>>x>>y;
if(mat.g[x][y] == 0) mat.g[x][y] = 2;
else puts("ERROR!");
// mat.print();
}
mat.print();
return 0;
}
K、Alice和Bob的爱恨情仇(mid - hard)【构造】
我们玩几组样例就会发现如果 s < 2 ∗ n s < 2 * n s<2∗n 就一定不能找到满足条件的答案。
因为我们需要找到一个 k k k ,使得序列里所有的数都不能凑出来它,所以我们很容易就想到一个构造方法: k k k 为一个很小的值,使得其他序列里的数都比它大,这样他们加起来就更不可能凑出来它,因为我们发现小于二倍的 n n n 时就一定没有解,所以我们一定能构造出来 n n n 个 2 2 2 的序列,我们只需要将 k k k 取为比 2 2 2 小的数 —— 1 1 1 即可!。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
const int N = 1e6 + 7, M = 1e7 + 7,INF = 0x3f3f3f3f;
int n,s;
int main(){
scanf("%d%d",&n,&s);
if(s < 2 * n){
puts("NO");
}
else {
puts("YES");
for(int i = 1;i < n;++i)
printf("2 "),s -= 2;
printf("%d\n",s);
puts("1");
}
return 0;
}
L、石同学的树(hard)【树hash】
我们知道可以用前序遍历和中序遍历确定一个二叉树,也就是说两个结构相同的二叉树的前序遍历和中序遍历一定相同,我们需要找到结构相同的二叉树但是编号不一样,所以我们可以先来一次dfs给这颗二叉树重新赋值为DFS序,然后再找到他们的前序遍历和中序遍历,然后我们可以采取hash算法判断是否相同或者直接用set去重即可,因为本题要求的是结构不同的树的个数。set是由红黑树实现的,所有的操作都是 O ( l o g n ) O(logn) O(logn),整体时间复杂度为
-
set: O ( m n l o g n ) O(mnlogn) O(mnlogn),代码更好写一点但是多了一个常数,本来想卡一下 O ( m n l o g n ) O(mnlogn) O(mnlogn)的但是我们生成数据的 p y t h o n python python 代码生成的是一个随机的二叉树,跑10000个点就跑了十几分钟,懒得跑更大的数了hhh。
-
hash: O ( n m ) O(nm) O(nm)。
我们同样可以使用树 h a s h hash hash 来解决这个问题,树hash算法可以判重所有的树,不管是二叉树还是普通的 n n n 个节点 n − 1 n-1 n−1 条边的树,做法比较普遍。
具体可以参考下面这篇博客:树hash - 树的同构,我在博客中使用的是几乎不可能出现 h a s h hash hash 冲突的一种优化 h a s h hash hash 算法,时间复杂度同样为 O ( n m ) O(nm) O(nm) 。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m;
cin >> m;
set <vector <int>> s;
for (int i = 1; i <= m; ++i) {
int n, r;
cin >> n >> r;
vector <pair <int, int>> e(n + 1);
for (int i = 1; i <= n; ++i) {
int a, b, c;
cin >> a >> b >> c;
e[a] = make_pair(b, c);
}
vector <int> id(n + 1);
int tot = 0;
function <void(int)> dfs1 = [&] (int cur) {
if (cur == 0)
return ;
id[cur] = ++tot;
dfs1(e[cur].first);
dfs1(e[cur].second);
};
dfs1(r);
vector <int> seq;
function <void(int)> dfs2 = [&] (int cur) {
if (cur == 0)
return ;
dfs2(e[cur].first);
seq.emplace_back(id[cur]);
dfs2(e[cur].second);
};
dfs2(r);
s.insert(seq);
}
cout << s.size() << endl;
}
M、简单数论(hard)【欧拉函数+快速幂+拓展欧几里得+因数分解+解同余方程(doge)】
数据较弱,你只需要枚举到7就能找到答案AC了hhh
题解差不多都在题面上了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 5000007;
ll n, m;
vector<int>factor;
vector<int> get_factor(ll n)
{
vector<int>res;
for(int i = 1; i * i <= n; ++ i) {
if(n % i == 0) {
res.push_back(i);
if(i != n / i) {
res.push_back(n / i);
}
}
}
return res;
}
int qpow(int a, int b, int c)
{
int res = 1;
while(b){
if(b & 1) res = ((ll)res * a) % c;
a = ((ll)a * a) % c;
b >>= 1;
}
return res % c;
}
int main()
{
scanf("%lld", &n);
ll phi_m = n - 1;
factor = get_factor(phi_m);
for(int i = 2; i < n; ++ i) {
bool flag = 1;
for(int j = 0; j < factor.size(); ++ j) {
if(factor[j] != phi_m && qpow(i, factor[j], n) == 1) {
flag = 0;
break;
}
}
if(flag) {
printf("%d\n", i);
break;
}
}
return 0;
}