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。

输出格式 

  输出一个整数表示答案

 

“贡献值法”:计算每个字母的"贡献" :

 

 

 注意:

  1. 结果用long long型来存储结果
  2. 这题只有复杂度为\color{Red} O\left ( n \right )才能通过所有测试点

 

 贡献值计算:(当前位置-前一个重复位置)*(后一个重复位置-当前位置)

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 }

 

 

 

posted @ 2022-03-28 20:10  Stickycat  阅读(154)  评论(0)    收藏  举报