蓝桥杯2023年第十四届省赛真题题解

日期统计

纯暴力 + 剪枝(注意日期年月日的一些特性)

#include <iostream>
#include <unordered_set>
using namespace std;
const int nums[] = {0, 5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3, 8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6, 1, 4, 0, 1,
    0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3};
const int n = 100;
const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unordered_set<string> st;
int main(int argc, const char * argv[]) {
    int sum = 0;
    for (int a = 1; a <= n; ++a) {//year
        if (nums[a] == 2) {
            for (int b = a + 1; b <= n; ++b) //year
                if (nums[b] == 0) {
                    for (int c = b + 1; c <= n; ++c) {//year
                        if (nums[c] == 2) {
                            for (int d = c + 1; d <= n; ++d) {//year
                                if (nums[d] == 3) {
                                    for (int e = d + 1; e <= n; ++e) {//month
                                        if (nums[e] == 0 || nums[e] == 1) {
                                            for (int f = e + 1; f <= n; ++f) {//month
                                                if ((nums[e] == 0 && nums[f] != 0) || (nums[e] == 1 && nums[f] <= 2)) {
                                                    for (int g = f + 1; g <= n; ++g) {//day
                                                        if (nums[g] <= 3) {
                                                            for (int h = g + 1; h <= n; ++h) {//day
                                                                if (nums[g] == 0 && nums[h] == 0) {
                                                                    continue;
                                                                }
                                                                string str = "2023";
                                                                int month = nums[e] * 10 + nums[f];
                                                                int day = nums[g] * 10 + nums[h];
                                                                str += to_string(nums[e]) + to_string(nums[f]);
                                                                str += to_string(nums[g]) + to_string(nums[h]);
                                                                if (day <= days[month]) {
                                                                    if (st.count(str)) {
                                                                        continue;
                                                                    }
                                                                    st.insert(str);
                                                                    puts(str.c_str());
                                                                    ++sum;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            
        }
    }
    printf("%d\n",sum);
    return 0;
}

01串的熵

模拟题

#include <iostream>
#include <cmath>
using namespace std;
const int n = 23333333;
const double eps = 1e-4;
const double ans = 11625907.5798;
double f(int sum1, int sum0){
  double res = 0;
  double p1 = (double)sum1 / (double)n;
  double p0 = (double)sum0 / (double)n;
  res += p1 * sum1 * log2(p1);
  res += p0 * sum0 * log2(p0);
  return res;
}
int main()
{
  for (int i = 0; i <= n >> 1; ++ i)
  {
    if (fabs(-f(n - i, i) - ans) <= eps) 
    {
      cout << i;
      break;
    }
  }
  return 0;
}

冶炼金属

解法一:思维

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
int n;
int maxx = 0, minn = 1e9;
int main()
{

  cin >> n;
  for (int i = 0; i < n; ++ i)
  {
    int a, b;
    cin >> a >> b; // a / v = b; a = b * v;
    maxx=max(maxx,a/(b+1) + 1) , minn=min(minn,a/b);
  }
  cout << maxx << " " << minn;
  return 0;
}

解法二:二分

飞机降落

dfs暴力

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11;
int t[N], d[N], l[N];
int st[N];
int n;
int ans;
bool dfs(int cnt, int time)
{
  if (cnt == n) 
  {
   return true;
  }
  for (int i = 0; i < n; ++ i)
  {
    if (st[i] == 0 && time <= t[i] + d[i])
    {
      st[i] = 1;
      cout << dfs(cnt + 1, max(time, t[i]) + l[i]);
      st[i] = 0;
    }
  }
  return false;
}
void solution()
{
  ans = 0;
  memset(st, 0, sizeof st);
  memset(t, 0, sizeof t);
  memset(l, 0, sizeof l);
  memset(d, 0, sizeof d);
  cin >> n;
  for (int i = 0; i < n; ++ i)
  {
    cin >> t[i] >> d[i] >> l[i];
  }
  if (dfs(0, 0)) cout << "YES" << endl;
  else cout << "NO" << endl;
  return;
}
int main()
{
  int T;
  cin >> T;
  while (T --) solution();
  return 0;
}

接龙数列

dp

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int dp[N];
int main()
{
    int n;
    cin >> n;
    int ans = 0;
    for (int i = 0; i < n; ++ i)
    {
        string s;
        cin >>s;
        int a = s[0] - '0', b = s.back() - '0';
        dp[b] = max(dp[a] + 1, dp[b]);
        ans = max(ans, dp[b]);
    }
    cout <<n - ans;
    return 0;
}

岛屿个数

解法一:染色 + 合并 + 统计

和常规的染色题目相比, 本题多了一个合并的步骤, 染色的时候通过八联通将各个岛屿分开, 并且将海水标记为2, 如果后续遍历地图的时候遇到了0, 说明这里是海水八联通进不来的地方, 直接用岛屿的标志1去合并这一整个镂空的岛屿 , 类似这样把这个岛里面铺满;
在这里插入图片描述
如果像下面这样, 外面的岛没有把里面的岛完全包围的情况, 那么也符合我们的预期, 他们是两个岛屿, 在dfs4(四联通)的时候会统计成两个岛屿
在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 52;
char g[N][N];
int T, n, m;
int ans;
int dx8[] = {1, 0, -1, 0, 1, 1, -1, -1}, dy8[] = {0, 1, 0, -1, 1, -1, 1, -1};

int dx4[] = {0, 1, 0, -1}, dy4[] = {1, 0, -1, 0};

void dfs4(int x, int y)
{
    g[x][y] = '2';
    int xx, yy;
    for (int i = 0; i <4; ++ i)
    {
        xx = x + dx4[i], yy = y + dy4[i];
        if (g[xx][yy] == '1' && xx >= 0 && xx <= n && yy >= 0 && yy <= m)
        {
            dfs4(xx, yy);
        }
    }
}
void dfs8(int x, int y)
{
    g[x][y] = '2';
    for (int i = 0; i < 8; ++ i)
    {
        int xx = x + dx8[i], yy = y + dy8[i];
        if (g[xx][yy] == '0' && xx >= 0 && xx <= n + 1 && yy >= 0 && yy <= m + 1 )
        {
            dfs8(xx, yy);
        }
    }
}
int main()
{   
    cin >> T;
    while (T --)
    {
        ans = 0;
        cin >> n >> m;
        //初始化
        for (int i = 0; i < n + 2; ++ i)
            for (int j = 0; j < m + 2; ++ j)
                g[i][j] = '0';
        // io
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++ j) 
                cin >> g[i][j];
                
        //染海水
        dfs8(0, 0);
        //合并, 海水进不来的地方说明被岛屿全包围了, 那么直接变成1合并成一个岛屿
        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
                if (g[i][j] == '0') g[i][j] = '1';
        }
        
        //统计岛屿数量
        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
                if (g[i][j] == '1') 
                {
                    ans ++;
                    dfs4(i, j);
                }
        }
        cout << ans << endl;
    }
    return 0;
}

解法二: 给岛屿染色, 统计有多少种不同的颜色

学习这个博主的
坑: 多组测试数据记得memset g 和st, 我就是没有memset st 一直wa;
思路:

  • 给每个岛屿染上不同的颜色(用四联通 dfs4)
  • 通过海水进行八连通dfs8遍历岛屿数目, 记得dfs8中将遍历过的海水从0 变为 -1 , 否则dfs会爆内存
#include<iostream>
#include<cstring>
using namespace std;
const int N = 52;
int g[N][N];
int T, n, m;
int ans, k = 2;
int dx8[] = {1, 0, -1, 0, 1, 1, -1, -1}, dy8[] = {0, 1, 0, -1, 1, -1, 1, -1};
int dx4[] = {0, 1, 0, -1}, dy4[] = {1, 0, -1, 0};
int st[N];
void dfs4(int x, int y)
{
    g[x][y] = k;
    int xx, yy;
    for (int i = 0; i < 4; ++ i)
    {
        xx = x + dx4[i], yy = y + dy4[i];
        if (g[xx][yy] == 1 && xx >= 1 && xx <= n && yy >= 1 && yy <= m)
        {
            dfs4(xx, yy);
        }
    }
}
void dfs8(int x, int y)
{
    g[x][y] = -1;
    for (int i = 0; i < 8; ++ i)
    {
        int xx = x + dx8[i], yy = y + dy8[i];
        if (xx >= 0 && xx <= n + 1 && yy >= 0 && yy <= m + 1)
        {
            if (g[xx][yy] >= 2 && !st[g[xx][yy]])
            {
                st[g[xx][yy]] = 1;
                ans ++;
            }
            if (g[xx][yy] == 0) dfs8(xx, yy);
        }
    }
}
int main()
{   
    cin >> T;
    while (T --)
    {
        ans = 0;
        k = 2;
        cin >> n >> m;
        //初始化
        memset(g, 0, sizeof g);
        memset(st, 0, sizeof st);
        // io
        for (int i = 1; i <= n; ++i)
        {
            string x;
            cin >> x;
            for (int j = 1; j <= m; ++ j) 
            {
                
                g[i][j] = x[j - 1] - '0';
            }
        }
            
                
        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
            {
                if (g[i][j] == 1) 
                {
                    dfs4(i, j);
                    k ++;
                }
            }
        }
        dfs8(0, 0);
        cout << ans << endl;
    }
    return 0;
}

子串简写

后缀和
记得ans开long long

#include<iostream>
#include<string>
#include <queue>
using namespace std;
const int N = 5e5 + 10;
int k;
string s;
char c1, c2;
int cnt[N];
int main()
{
    long long ans = 0;
    cin >> k >> s >> c1 >> c2;
    for (int i = s.size() - 1; i >= 0; --i)
    {
        cnt[i] = cnt[i + 1];
        if (s[i] == c2) cnt[i] ++;
    }
    for (int i = 0; i + k - 1 < s.size(); ++ i)
    {
        if (s[i] == c1) ans += cnt[i + k - 1];
    }
    cout << ans;
    return 0;
}

整数删除

数组模拟双向链表, 优先队列; 这题考的数据结构
注意模拟双向链表的时候 r[0] = 1; l[n + 1] = n;


#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long LL;
typedef pair<LL, LL> PLL;
int n, k;
LL a[N], l[N], r[N];

void del(LL i)
{
    //把i的值加到i的左边和右边
    a[l[i]] += a[i], a[r[i]] += a[i];
    //i的左边指向i的右边, i的右边指向i的左边, 
    r[l[i]] = r[i];
    l[r[i]] = l[i];
}

int main()
{
    cin >> n >> k;
    r[0] = 1; l[n + 1] = n;
    priority_queue<PLL, vector<PLL>, greater<PLL>> q;
    for (int i = 1; i <= n; ++ i) 
    {
      //正好缺一个r[0] = 1; l[n + 1] = n;
        l[i] = i - 1; r[i] = i + 1;
        cin >> a[i];
        q.push({a[i], i});
    }
    
    while (k -- )
    {
        PLL u = q.top();
        q.pop();
        auto [v, vi] = u;
        if (v != a[vi]) 
        {
            q.push({a[vi], vi});
            k ++ ;
        }
        else del(vi);
    }
    
    for (int i = r[0]; i != n + 1; i = r[i])
    {
        cout << a[i] << " ";
    }
    return 0;
}

砍树(LCA + 树上差分)

树上差分就是要用到LCA的板子
本题题解
树上边差分博客
树上点差分博客
注意点差分和边差分的区别;
具有抽象思维: 联通块类的题目每个点连接父节点的那条边的贡献度是1
数据范围又开小了,导致一直是前6个点能过 后面十几个点过不了(看到这里前几个能过后面过不了就应该想到是数据范围开小了的)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 5e5 + 10;
int h[N], e[N], ne[N], idx;
int s[N], ans = -1;
int d[N], f[N][21];
int n, m;
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int fa)
{   
    if(d[u]) return;
    //预处理d[]
    d[u] = d[fa] + 1;
    f[u][0] = fa;
    //预处理f[]
    for (int k = 1; k <= 20; ++k)
    {
        f[u][k] = f[f[u][k - 1]][k - 1];
    }
    for (int i = h[u]; ~i; i = ne[i])
    {
        int v = e[i];
        if(v == fa) continue;
        dfs(v, u);
    }
}
int lca(int a, int b)
{
    if (d[a] < d[b]) swap(a, b);
    for (int k = 20; k >= 0; -- k)
    {
        if (d[f[a][k]] >= d[b]) a = f[a][k];
    }
    if (a == b) return a;
    for (int k = 20; k >= 0; -- k)
    {
        if (f[a][k] == f[b][k]) continue;
        a = f[a][k], b = f[b][k];
    }
    
    return f[a][0];
}

void update(int u, int fa, int id, int &ans)
{
    for (int i = h[u]; ~i; i = ne[i])
    {
        int v = e[i];
        if (v == fa) continue;
        update(v, u, i, ans);
        s[u] += s[v];
    }
    if (s[u] == m)
    {
        ans = max(ans, id / 2 + 1);
    }
}



int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; ++i)
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    //在lca前!!预处理d[]和f[];
    dfs(1, 0);
    for (int i = 0; i < m; ++ i)
    {
        int x, y;
        cin >> x >> y;
        s[x] ++, s[y] ++, s[lca(x, y)] -= 2;
    }
    //根节点为自定义的哨兵0, f[1] = 0;
    //一个图从哪里开始遍历都行, 哪里拎起来都是一棵树
    update(1, 0, 0, ans);
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值