Recording
一般ACM或者笔试题的时间限制是1秒或2秒。
在这种情况下,C++代码中的操作次数控制在 1e7∼1e8 为最佳,O(n)的极限就在10^8左右。
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:

跳跃:


1 #include<stdio.h>
2 #include<iostream>
3 #include<math.h>
4 #include<string>
5 using namespace std;
6 int arr[110][110];//存储每个点的权值
7 int av[110][110];//存储从起点到当前位置的最大的总权值
8 int n, m;
9 int main()
10 {
11 cin >> n >> m;
12 for (int i = 1; i <= n; i++)
13 {
14 for (int j = 1; j <= m; j++)
15 {
16 cin >> arr[i][j];
17 if(i==1&&j==1)av[1][1] = arr[1][1];//初始化1,1位置
18 else av[i][j] = -10000;
19 }
20 }
21 for (int i = 1; i <= n; i++)
22 {
23 for (int j = 1; j <= m; j++)
24 {
25 if (i == 1 && j == 1)continue;
26 for (int k = 0; k <= 3; k++)
27 {
28 for (int h = 0; h <= 3; h++)
29 {
30 if (h == 0 && k == 0)continue;//对点自身不判断
31 if (i - k > 0 && j - h > 0 && av[i - k][j - h] + arr[i][j] > av[i][j]) {//判断自身以外的前面的8个可以到达该位置的点
32 av[i][j] = av[i - k][j - h] + arr[i][j];
33 }
34 }
35 }
36 }
37 }
38 cout << av[n][m] << endl;
39 return 0;
40 }
直线

思路:通过斜率k与b可以确定一条直线,先得k,再通过y=kx+b得到b。map映射模板,让确定的值为1。
1 #include <bits/stdc++.h>
2 #include <iostream>
3 using namespace std;
4 struct Point
5 {
6 double x, y;
7 } p[25 * 25]; //存下每一个点
8 map<pair<double, double>, int> mp; //存斜率k和截距b
9 int main()
10 {
11 int cnt = 0;
12 for (int i = 0; i < 20; i++)
13 {
14 for (int j = 0; j < 21; j++)
15 {
16 p[cnt].x = i;
17 p[cnt++].y = j;
18 }
19 }
20 int ans = 20 + 21;
21 for (int i = 0; i < cnt; i++)
22 {
23 for (int j = 0; j < cnt; j++)
24 {
25 //两点的直线与坐标轴平行或共点
26 if (p[i].x == p[j].x || p[i].y == p[j].y)
27 continue;
28 //斜率和截距
29 double k = (p[j].y - p[i].y) / (p[j].x - p[i].x);
30 double b = (p[j].x * p[i].y - p[j].y * p[i].x) / (p[j].x - p[i].x);
31 //b=(x1*y2-x2*y1)/(x1-x2) 只能用两点,不能求k后代入解b,会精度损失
32
33 if (mp[make_pair(k, b)] == 0)
34 {
35 mp[make_pair(k, b)] = 1;
36 ans++;
37 }
38 }
39 }
40 cout << ans << endl;
41 return 0;
42 }
货物摆放:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ll n = 2021041820210418L;
ll ans = 0;
for(ll i = 1;i*i*i <=n;i++){
if(n%i != 0) continue; //若是i不是n的因子,就continue。注意必须是 ll 型
ll tmp = n / i;
for(ll j = 1;j*j<= tmp;j++){
if(tmp % j != 0) continue;//若j不是(n/i)的因子,就continue。
ll k = tmp / j;
if(k < j || i > j) continue;//如果三个数不是递增,就continue,去重
if(i == j && i == k) ans +=1;
else if(i == j || i==k || j==k) ans +=3;
else ans += 6;
}
}
cout<< ans <<endl;
return 0;
}
// 更新暴力解法:
// 其实不用分解素因子,直接 O ( n ) O(\sqrt{n}) O(n
// )暴力求出所有因子,发现只有128个,然后直接三层循环判断三个因子相乘是否为n即可:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n=2021041820210418,ans=0;
vector<ll>fac;
int main()
{
for(ll i=1;i*i<=n;i++) //注意这里ll i不能是int 否则永远达不到n
if(n%i==0)fac.push_back(i),fac.push_back(n/i);
int sz=fac.size(); // 128个因子
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++)
for(int k=0;k<sz;k++)
if(fac[i]*fac[j]*fac[k]==n)ans++;
printf("%lld\n",ans);
return 0;
}
砝码称重


#include<bits/stdc++.h>
using namespace std;
int dp[105][100000];
int main()
{
int n,ans=0;
cin>>n;
int a[n];
int sum = 0;
for (int i = 0; i < n; i++)
{
cin >> a[i];
sum += a[i];
}
dp[0][a[0]] = 1; //开始只有一个砝码 可以称出重量a[0]
for(int i=1;i<n;i++) //有两个以上砝码
{
for (int j = 1;j<=sum;j++)
{
dp[i][j] = dp[i - 1][j]; //i-1个砝码能称出j,则i个砝码一定也能称出j
//类01背包问题 复制上一层的状态
}
dp[i][a[i]] = 1;
for(int j=1;j<=sum;j++) // j表示能称出的重量
{
if(dp[i-1][j])
{
dp[i][j + a[i]] = 1;
dp[i][abs(j - a[i])] = 1;
//01背包更新此时的状态
}
}
}
for(int i=1;i<=sum;i++)
if(dp[n-1][i])
ans++;
cout<<ans;
return 0;
}
蓝桥侦探

输入输出样例
示例 1
输入:
4 5
1 2
1 3
2 3
3 4
1 4
输出:
2
普通的并查集维护的关系是: 朋友的朋友是朋友,即如果 A ~ B 是一对朋友, B ~ C 是一对朋友, 那么A~C 是一对朋友。但如果我们需要维护这样一个关系“朋友的朋友是朋友,朋友的敌人是敌人,敌人的敌人是朋友”,普通的并查集就无能为力了。这题也是如此,普通的并查集只能维护多少人是在一个大厅,但是题目给的是不在一个大厅,并且只有俩个大厅,那么就要维护和同一个人不在一个大厅的俩人在一个大厅,就不太行了,因此,需要引入种类并查集。
种类并查集又叫做扩展域并查集,也就是我们扩展出一个域 i+n ,作为 i 号的 点的敌人,那么同时和 i+n 相连的点就是 i 号点的敌人,那么他们之间也就是朋友,这样就可以维护 ”敌人的敌人是朋友“这类关系了。
#include <iostream>
using namespace std;
int n, m;
int fa[1000000];////注意:将并查集范围扩展一倍
int find(int x)//find是找到一个集合的根节点,fa[]是某个结点的父节点,不是一个概念
{
if(x==fa[x])
return x;
else
return find(fa[x]);
}
void join(int a,int b)
{
int fx = find(a);//查找函数确定是否在同一集合
int fy=find(b);
if(fx!=fy)
fa[fx] = fy;
}
int main()
{
cin >> n >> m;
int res=0;
for (int i = 1; i <= 2*n; i++)//注意:将并查集范围扩展一倍
fa[i] = i;
for (int i = 1; i <= m;i++)
{
int x, y;
cin >> x >> y;//x,y是敌人
if(res)
continue;
if (find(x) == find(y) || find(x + n) == find(y + n)) //x,y在同一集合或 x敌,y敌在n敌人在同一集合
{
res = x;
}
else
{
join(x, y + n);//x和y的敌人属于同一个集合
join(y, x + n);//y和x的敌人属于同 一个集合
}
}
cout << res << endl;
return 0;
}
小明的衣服
题目描述
小明买了 n 件白色的衣服,他觉得所有衣服都是一种颜色太单调,希望对这些衣服进行染色,每次染色时,他会将某种颜色的所有衣服寄去染色厂,第 iii 件衣服的邮费为ai元,
染色厂会按照小明的要求将其中一部分衣服染成同一种任意的颜色,之后将衣服寄给小明, 请问小明要将 n件衣服染成不同颜色的最小代价是多少?

题目分析
方法一:类哈夫曼树 优先队列
#include<iostream> using namespace std; typedef long long ll; #include<queue> ll ans,x,a,b,c,n; int main() { cin>>n; priority_queue<ll,vector<ll>,greater<ll>> q;//升序队列 for(int i=0;i<n;i++){ cin>>x; q.push(x);//进队 } while(q.size()>1){ //注意q.size()>1
a=q.top();q.pop(); b=q.top();q.pop(); c=a+b; ans+=c; q.push(c); } cout<<ans<<endl; return 0; }
解立方根

思路:二分搜索
#include <bits/stdc++.h>
using namespace std;
int n;
double ans;
const double esp = 1e-12;
//e=1e-12:保留三位有效数字则n需要确定到第4位,n^3则为1e-12
int main()
{
int t;
cin >> t;
for (int i = 0; i < t; i++)
{
cin >> n;
double l = 0;
double r = 100000;
double mid;
while (l <= r)
{
mid = (l + r) / 2;
if(fabs(mid*mid*mid-n)<=esp)
{
// ans = mid;
break;
}
else if(mid*mid*mid<n)
{
l = mid + 0.0001;
// ans = mid;
}
else if (mid * mid * mid > n)
r = mid - 0.0001;
}
cout << mid << endl;
cout << fixed << setprecision(3) << mid << endl;
}
return 0;
}
走迷宫

输入:
5 5 1 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 5 5
输出:8
1 #include <bits/stdc++.h>
2 using namespace std;
3 int n, m;
4 int xone, xtwo, yone, ytwo;
5 int vis[101][101], g[101][101];
6 int dir[4][4] = {{1,0}, {-1,0}, {0,1}, {0,-1}};
7 struct node
8 {
9 int x, y, dis;
10 } Node;
11 bool check(int x,int y)
12 {
13 if(x<1||y<1||x>n||y>m||vis[x][y]==1||g[x][y]==0)
14 return false;
15 return true;
16 }
17 int bfs(int x,int y)
18 {
19 queue<node> q;
20 Node.x=x;
21 Node.y = y;
22 Node.dis = 0;
23 q.push(Node);
24 while(!q.empty())
25 {
26 node top = q.front();//.front() 优先队列是.top()
27 q.pop();
28 if(top.x==xtwo&&top.y==ytwo)// 如果该点就是要遍历的点,就结束
29 return top.dis;
30
31 // 如果该点不是终点,看看该点可以往那儿走
32 for (int i = 0; i < 4; i++)
33 {
34 int nx = top.x + dir[i][0];
35 int ny = top.y + dir[i][1];
36 if(check(nx,ny))// 查看当前的点是否可以走,如果可以走的话将其入队
37 {
38 Node.x=nx;
39 Node.y = ny;
40 Node.dis = top.dis + 1; //这里不要写node.dis=top.dis++; 我好蠢!
41 q.push(Node);
42 vis[nx][ny] = 1;
43 }
44 }
45
46 }
47 return -1;//记得给返回值
48 }
49 int main()
50 {
51 cin >> n >> m;
52 for (int i = 1; i <= n;i++)
53 {
54 for (int j = 1; j <= m;j++)
55 {
56 cin >> g[i][j];
57 }
58 }
59 cin >> xone >> yone>>xtwo >> ytwo;
60 cout<<bfs(xone, yone)<<endl;
61
62 return 0;
63 }
亿万富翁(单调栈)

思路:单调栈,注意左边第一栋,右边第一栋,都是由中心往两侧发散的,一开始不能理解到底哪边是第一栋。。。。。
单调栈简介
1 #include<bits/stdc++.h>
2 #define double long double
3 using namespace std;
4 int n;
5 int h[7000100], ans[7000100],res[70001000];
6 stack<int> stk;//单调递增栈(存下标)
7 int main()
8 {
9 cin >> n;
10 for (int i = 1; i <= n;i++)
11 cin >> h[i];
12 //左侧第一个(以当前值中心) 从前往后扫
13 for (int i = 1; i <= n;i++)
14 {
15 if(stk.size() && h[stk.top()] <= h[i])//当栈顶<当前值时 小的值全部出栈
16 stk.pop();
17 if(stk.size()==0)//左侧没有比当前值大的
18 ans[i] = -1;
19 else
20 ans[i] = stk.top();//左侧第一个比俺大的数的下标是栈顶
21 stk.push(i);//当前值入栈
22 }
23 while(!stk.empty())//清空
24 stk.pop();
25
26 //右侧找第一个(以当前值为中心) 从后往前扫
27 for (int i = n; i >=1;i--)
28 {
29 while(stk.size() && h[stk.top()] <= h[i])
30 stk.pop();
31 if(stk.size()==0)
32 res[i] = -1;
33 else
34 res[i] = stk.top();
35 stk.push(i);
36 }
37 for (int i = 1; i <= n;i++)
38 cout << ans[i] << " ";
39 cout << endl;
40 for (int i = 1; i <= n;i++)
41 cout << res[i] << " ";
42 cout << endl;
43
44 return 0;
45 }
[蓝桥杯2020初赛] 子串分值
题目描述
对于一个字符串S ,我们定义S 的分值f (S ) 为S 中恰好出现一次的字符个数。
例如f (”aba”) = 1, f (”abc”) = 3, f (”aaa”) = 0。
现在给定一个字符串S [0..n - 1](长度为n),请你计算对于所有S 的非空子串S [i.. j](0 ≤ i ≤ j < n), f (S [i .. j]) 的和是多少。
输入格式
输入一行包含一个由小写字母组成的字符串S 。
对于20% 的评测用例,1 ≤ n ≤ 10;
对于40% 的评测用例,1 ≤ n ≤ 100;
对于50% 的评测用例,1 ≤ n ≤ 1000;
对于60% 的评测用例,1 ≤ n ≤ 10000;
对于所有评测用例,1 ≤ n ≤ 100000。
输出格式
输出一个整数表示答案
“贡献值法”:计算每个字母的"贡献" :

注意:
- 结果用long long型来存储结果
- 这题只有复杂度为
才能通过所有测试点

贡献值计算:(当前位置-前一个重复位置)*(后一个重复位置-当前位置)
f(a)=(1-0)*(3-1)=2 : a,ab
f(b)=(2-0)*(4-2)=4: ab,aba,b,ba
f(a)=(3-1)*(6-3)=6: babc,bab,ba,a,ab,abc
f(b)=(4-2)*(6-4)=4: abc,ab,b,bc
f(c)=(5-0)*(6-5)=5: ababc,babc,abc,bc,c
共:2+4+6+4+5=21
代码实现:
1 #include<iostream> 2 #include<cstring>//memset() 3 using namespace std; 4 typedef long long ll; 5 const int N=1e5+10; 6 7 //子串分值 8 int main() 9 { 10 string s; 11 cin>>s; 12 ll ans=0,n=s.size(); 13 ll l[N],r[N],vis[27];//字符左,右边相同字符的下标 14 char a[N]; 15 for(int i=1;i<=n;i++) a[i]=s[i-1]; 16 memset(vis,0,sizeof(vis)); 17 for(int i=1;i<=n;i++){ 18 int k=a[i]-'a'; 19 l[i]=vis[k]; 20 vis[k]=i; 21 } 22 for(int i=0;i<27;i++) vis[i]=n+1; 23 for(int i=n;i>=1;i--){ 24 int k=a[i]-'a'; 25 r[i]=vis[k]; 26 vis[k]=i; 27 } 28 for(int i=1;i<=n;i++){ 29 ans+=(i-l[i])*(r[i]-i); 30 } 31 cout<<ans<<endl; 32 return 0; 33 }



浙公网安备 33010602011771号