比赛题目共2套,其中初赛题1套,复赛2题。

比赛时间: 10:50 - 12:00 a.m。

Part 0x01 过程-Process

\(8:40\,a.m.\) 做初赛题目;

\(10:40\,a.m.\) 拿到题目;

\(10:51\,a.m.\) 先写 \(\text{T2}\),发现是初赛考过的题目,但时限变小;

\(11:20\,a.m.\)\(\text{T2}\) 上花了太久,没调出来,赶紧写 \(\text{T1}\)

\(11:35\,a.m.\) 终于把 \(\text{T1}\) 写完了,估计能过。

\(11:36\,a.m.\) 接着看 \(\text{T2}\),突然发现理解错题目了,但是样例 \(2\) 没过……

\(12:00\,a.m.\) \(\text{T2}\) 发现了一些问题,但是样例 \(2\) 还是没过。

总分 \(= 100pts + 30pts = 130pts\)

Part 0x02 初赛注意事项-Theory

  1. \(∨\) 代表或运算,即 ||
  2. \(∧\) 代表与运算,即 &&
  3. \(?\) 代表非运算,即 !
  4. 文件型病毒传染的主要对象是 EXECOM 文件
  5. Linux下可执行文件没有后缀名
  6. 田忌赛马思想:
    • 对手必胜,就用最小的马输给对手
    • 对手必败,就用最小的马赢对手
    • 我方必胜,就用最小的马赢对手
    • 我方必败,就用最小的马输给对手

Part 0x03 复赛题目-Problem

T1 幸运质数

题链

正经做法

使用埃式筛筛出 \([1, m]\) 内的所有质数,就可以快速判断了。

My Code:

// g++ "prime.cpp" -Wall -std=c++14 -o "prime"
// ./"prime"

#include <bits/stdc++.h>

using namespace std;
 
int vis[10000001]; 

void GetPrime(int n)
{
    memset(vis, 0, sizeof(vis));
  
    vis[1] = 1;
    vis[2] = 0;
  
    for (int i = 2; i <= n; i++) 
	{
        if(vis[i] == false)
        {
            for (int j = i * i; j <= n; j += i)
            {
                vis[j] = 1;
			}
		}
        if(i * i > n)
        {
            break;
		}
    }
}

bool Check(int num)
{
    while(num > 0)
    {
        if(vis[num] == true)
        {
            return false;
        }
        num /= 10;
    }
  
    return true;
}

int main()
{
    freopen("prime.in", "r", stdin);
    freopen("prime.out", "w", stdout);

    int n, m;

    scanf("%d %d", &n, &m);
  
    GetPrime(m);

    for(int i = n; i <= m; i++)
    {
//        printf("%d:%d\n", i, vis[i]);
        if(Check(i))
        {
            printf("%d\n", i);
        }
    }
  
    return 0;
}

时间复杂度 \(O(n\,log_2\,n)\)

打表

注意到 \([1, 10^7]\) 内只有 \(78\) 个幸运素数,直接打表即可。

打表器 (gen.cpp, 暴力即可):

// g++ "gen.cpp" -Wall -std=c++14 -o "gen"
// ./"gen"

#include <bits/stdc++.h>

using namespace std;

bool IsPrime(int n)
{
    if(n == 1)
    {
        return false;
    }

    for(int i = 2; i <= sqrt(n); i++)
    {
        if(n % i == 0)
        {
            return false;
        }
    }

    return true;
}

bool Check(int num)
{
    while(num > 0)
    {
        if(IsPrime(num) != true)
        {
            return false;
        }
        num /= 10;
    }
  
    return true;
}

int main()
{
    freopen("biao.txt", "w", stdout);

    for(int i = 1; i <= 1e7; i++)
    {
//        printf("%d:%d\n", i, vis[i]);
        if(Check(i))
        {
            printf("%d, ", i);
        }
    }
  
    return 0;
}
*/

提交代码 (prime.cpp, 按表输出):

// g++ "prime.cpp" -Wall -std=c++14 -o "prime"
// ./"prime"

#include <bits/stdc++.h>

using namespace std;

const int biao[100] = {0, 2, 3, 5, 7, 23, 29, 31, 37, 53, 59, 71, 73, 79, 233, 239, 293, 311, 313, 317, 373, 379, 593, 599, 719, 733, 739, 797, 2333, 2339, 2393, 2399, 2939, 3119, 3137, 3733, 3739, 3793, 3797, 5939, 7193, 7331, 7333, 7393, 23333, 23339, 23399, 23993, 29399, 31193, 31379, 37337, 37339, 37397, 59393, 59399, 71933, 73331, 73939, 233993, 239933, 293999, 373379, 373393, 593933, 593993, 719333, 739391, 739393, 739397, 739399, 2339933, 2399333, 2939999, 3733799, 5939333, 7393913, 7393931, 7393933};

int main()
{
    freopen("prime.in", "r", stdin);
    freopen("prime.out", "w", stdout);

    int n, m;

    scanf("%d %d", &n, &m);

    for(int i = 1; i <= 78; i++)
    {
        if(biao[i] >= n && biao[i] <= m)
        {
            printf("%d\n", biao[i]);
        }
    }
  
    return 0;
}

T2 学习

题链

初赛考过这题的暴力解法;

使邻接表存储有向图,使用拓扑排序,注意不能用普通队列,应用优先队列对门槛经验值排序。

My Code:

// g++ "study.cpp" -Wall -std=c++14 -o "study"
// ./"study"

#include <bits/stdc++.h>

using namespace std;

vector <int> G[100010];
int in[100010];
int a[100010], b[100010];

int n, m, w;

struct Node
{
    int id;
    int need;
};

struct Greater
{
    bool operator ()(Node a, Node b)
    {
        return a.need > b.need;
    }
};

void Kahn()
{
    priority_queue <Node, vector<Node>, Greater> S;
 
//    queue <int> S;
    vector <int> L;
  
    for(int i = 1; i <= n; i++)
    {
        if(in[i] == 0)
        {
            S.push((Node){i, a[i]});
        }
    }
    int ans = 0;
  
    while(S.empty() == false)
    {
        int u = S.top().id;
    
        S.pop();
        L.push_back(u);
        if (a[u] > w)
        {
            break;
        }
    
        ans++;
        w = w + b[u];
    
        for(int v : G[u])
        {
            in[v]--;
        
            if(in[v] == 0)
            {
                S.push((Node){v, a[v]});
            }
        }
    }
  
    printf("%d\n", ans);
}

int main()
{
    freopen("study.in", "r", stdin);
    freopen("study.out", "w", stdout);

    scanf("%d %d %d", &n, &m, &w);

    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }

    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &b[i]);
    }
  
    for(int i = 1; i <= m; i++)
    {
        int u, v;
    
        scanf("%d %d", &u, &v);
    
        G[u].push_back(v);
        in[v]++;
    }

    Kahn();
  
    return 0;
}

时间复杂度 \(O(n+m)\)

Other

题目描述

给出 \(n\) 根长度各异的木棍,求这些木棍能否头尾相连形成一个正方形?

\(1 \leq n \leq 100\)

题解

初赛考了这题的暴力,老师说会考写一下。

类似背包的思路, 设 \(dp_{i, j, k, l}\) 为考虑第 \(i\) 根木棍,当前第一条边长度为 \(j\),第二条边长度为 \(k\),第三条边长度为 \(l\) 是否可行

明显有转移式:

\[dp_{i, j, k, l} = dp_{i-1, j, k, l}\,|\,dp_{i-1, j-a_i, k, l}\,|\,dp_{i-1, j, k-a_i, l}\,|\,dp_{i-1, j, k, l-a_i} \]

// g++ "sticks.cpp" -Wall -std=c++14 -o "sticks"
// ./"sticks"

#include <bits/stdc++.h>

using namespace std;

int side;
int n;
int sticks[110];
bool dp[110][110][110][110];

void DP()
{
    memset(dp, 0, sizeof(dp));

    for(int i = 1; i <= n; i++)
    {
        dp[1][sticks[i]][0][0] = true;
        dp[1][0][sticks[i]][0] = true;
        dp[1][0][0][sticks[i]] = true;
    }
  
    for(int i = 2; i <= n; i++)
    {
        for(int j = side; j >= 0; j--)
        {
            for(int k = side; k >= 0; k--)
            {
                for(int l = side; l >= 0; l--)
                {
                    dp[i][j][k][l] = dp[i-1][j][k][l];
                    if(j >= sticks[i])
                    {
                        dp[i][j][k][l] |= dp[i-1][j - sticks[i]][k][l];
                    }
                    if(k >= sticks[i])
                    {
                        dp[i][j][k][l] |= dp[i-1][j][k - sticks[i]][l];
                    }
                    if(l >= sticks[i])
                    {
                        dp[i][j][k][l] |= dp[i-1][j][k][l - sticks[i]];
                    }
                }
            }
        }
    }
}

int main() 
{
    int T;

    scanf("%d", &T);

    while (T--) 
    {
        int length = 0;

        scanf("%d", &n);

        for(int i = 1; i <= n; i++)
        {
            scanf ("%d", &sticks[i]);
            length += sticks[i];
        }

        if(length % 4 != 0)
        {
            printf("no\n");
            continue;
        }

        side = length / 4;
        sort(sticks + 1, sticks + n + 1, greater<int>());

        if(sticks[1] > side)
        {
            printf("no\n");
            continue;
        }

        DP();
    
        if(dp[n][side][side][side] == true)
        {
            printf("yes\n");
        } 
        else
        {
            printf("no\n");
        }
    }
    return 0;
}

时间复杂度 \(O(n^4)\)

Part 0x04 总结-Summary

  1. 不要死磕,不要死磕,不要死磕(重要的事情说三遍)
  2. 注意代码细节
  3. 存图应使用邻接表
  4. 当数据小的时候,可以使用 打表 解决问题
posted on 2023-07-25 20:37  Carey_Chen  阅读(15)  评论(0)    收藏  举报