第十届“图灵杯”NEUQ-ACM程序设计竞赛题解(A-I)

比赛主页:第十届“图灵杯”NEUQ-ACM程序设计竞赛

文章目录

    • A. 有用的算法
    • B. 平衡数
    • C. 三角形
    • D. 文稿修订
    • E. 减肥计划
    • F. 吃包子
    • G. 数字鉴定
    • H. 线性变换
    • I. 试题排版

A. 有用的算法

直接判断是否升序或降序,可以调用STL函数is_sorted()简化写法

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) cin >> a[i], b[i] = a[i];
    reverse(b.begin(), b.end());
    if (is_sorted(a.begin(), a.end()) || is_sorted(b.begin(), b.end())) 
        cout << "erfen is useful!";
    else cout << "bukeyi";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

B. 平衡数

直接判断即可

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    string s;
    cin >> s;
    int a = s[0] - '0';
    int b = s[1] - '0';
    int c = s[2] - '0';
    int d = s[3] - '0';
    if (a + b == c + d) cout << "YES" << '\n';
    else cout << "NO" << '\n'; 
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    cin >> T;
    while (T--) solve();
    return 0;
}

C. 三角形

计算在 y = y p y=y_p y=yp 时的 x x x 区间,然后判断 x p x_p xp 是否在区间内即可

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    double xb, xc, yc;
    cin >> xb >> xc >> yc;
    int xp, yp;
    cin >> xp >> yp;
    if (yp >= yc) cout << "no" << '\n';
    else 
    {
        // 计算区间,我这里使用相似三角形推的公式
        double l = yp * xc / yc;
        double r = xb - (yp * (xb - xc) / yc);
        if (l < xp && r > xp) cout << "yes" << '\n';
        else cout << "no" << '\n';
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

D. 文稿修订

模拟,用 i s t r i n g s t r e a m istringstream istringstream 来依次获取每行的每个单词,然后对单词进行处理

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    string str;
    vector<string> vec;
    int ans = 0;
    string s1 = "NEUQ", s2 = "WOW NEUQ";
    while (getline(cin, str))
    {
        if (str == "#") break;
        vec.push_back(str);
        // 把str放进istringstream流,这样就能依次读取每个string了
        istringstream is(str);
        string s;
        while (is >> s)
        {
            if (s != s1)
            {
                // 把所有的字符都变成大写
                transform(s.begin(), s.end(), s.begin(), ::toupper);
                if (s == s1) ans++;
            }
        }
    }
    cout << ans << '\n';
    for (int i = 0; i < vec.size(); i++)
    {
        istringstream is(vec[i]);
        string s;
        while (is >> s)
        {
            if (s == s1) cout << s2 << ' ';
            else cout << s << ' ';
        }
        cout << '\n';
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

E. 减肥计划

深搜,枚举每一种合法选择,然后取最大值

#include
#define int long long
#define x first
#define y second
using namespace std;

int n, a, b, c;
int ans = 0;
struct node
{
    int x, y, z, w;
}nodes[20];

void dfs(int u, int x, int y, int z, int cnt)
{
    if (u == n)
    {
        ans = max(ans, cnt);
        return;
    } 
    // 如果能吃,就加上然后继续搜下一个
    if (x + nodes[u].x <= a && y + nodes[u].y <= b && z + nodes[u].z <= c)
        dfs(u + 1, x + nodes[u].x,  y + nodes[u].y, z + nodes[u].z, cnt + nodes[u].w);
    // 如果不能吃,就直接搜下一个
    dfs(u + 1, x, y, z, cnt);
}


void solve()
{
    cin >> n >> a >> b >> c;
    for (int i = 0; i < n; i++)
    {
        int x, y, z, w;
        cin >> x >> y >> z >> w;
        nodes[i] = {x, y, z, w};
    }
    dfs(0, 0, 0, 0, 0);
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

F. 吃包子

二分,对于每一个位置,找到它最前面的合法的位置,这样的一个区间,然后取最大值

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1), sum(n + 1), res(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    // sum是素包子的前缀和
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (a[i] == 0);
    // res是肉包子的前缀和
    for (int i = 1; i <= n; i++) res[i] = res[i - 1] + a[i];
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        int l = 0, r = i;
        while (l < r)
        {
            int mid = l + r >> 1;
            // 判断素包子的个数是否小于等于m
            if (sum[i] - sum[mid] <= m) r = mid;
            else l = mid + 1;
        }
        ans = max(ans, res[i] - res[l]);
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

G. 数字鉴定

差分,把所有的不优雅的数都标记上,然后对于每一个询问都去扫一遍,然后把答案存起来,这样如果有相同的询问就可以直接输出减少复杂度

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(1000010);
    int maxx = 0;
    for (int i = 0; i < n; i++)
    {
        int l, r;
        cin >> l >> r;
        // 差分操作
        a[l] ++, a[r + 1] --;
        // maxx是区间右边界的最大值
        maxx = max(maxx, r);
    }
    // 前缀和恢复原数组
    for (int i = 1; i < 1000010; i++) a[i] += a[i - 1];
    // s1是优雅的,s2是不优雅的
    set<int> s1, s2;
    while (m--)
    {
        int x;
        cin >> x;
        // 如果答案集合里面有就直接输出
        if (s1.count(x))
        {
            cout << "YES" << '\n';
            continue;
        }
        if (s2.count(x))
        {
            cout << "NO" << '\n';
            continue;
        }
        bool flag = true;
        // 扫描数组,maxx是区间的最大值,大于这个还没找到就肯定是优雅的了
        for (int i = x; i <= maxx; i += x)
            if (a[i])
            {
                flag = false;
                break;
            }
        // 判断完放入集合
        if (flag)
        {
            s1.insert(x);
            cout << "YES" << '\n';
        }
        else
        {
            s2.insert(x);
            cout << "NO" << '\n';
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

H. 线性变换

注意数据范围 T T T 很大,所以肯定会出现循环的情况,我们可以找到这个环,然后进行数学运算即可

#include
#define int long long
#define x first
#define y second
using namespace std;

void solve()
{
    int n, p, k, b, t;
    cin >> n >> p >> k >> b >> t;
    vector <int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    // s用来标记下标,如果标记存在还要插入就代表出现环了
    set<int> s;
    // vec用来存下标的执行顺序
    vector<int> vec;
    int x = 0;
    // 这个循环是为了找最小环
    while (t--)
    {
        if (s.count(p)) break;
        s.insert(p);
        vec.push_back(p);
        x = x + a[p];
        p = (k * p + b) % n;
    }
    // 如果发现没有环,直接输出即可
    if (t == -1)
    {
        cout << x;
        return;
    }
    // 如果发现有环了,就需要求出环的权值总和
    t++;
    int sum = 0, i;
    for (i = vec.size() - 1; i >= 0; i--)
    {
        sum += a[vec[i]];
        if (vec[i] == p) break;
    }
    x += (t / (vec.size() - i)) * sum;
    t %= (vec.size() - i);
    while (t--)
    {
        x += a[vec[i]];
        i++;
    }
    cout << x << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

I. 试题排版

经典DP求方案数,

状态表示: d p [ i ] dp[i] dp[i] i i i 这个数有多少拆分方案

状态转移: d p [ j ] = d p [ j ] + d p [ j − i ] dp[j] = dp[j] + dp[j - i] dp[j]=dp[j]+dp[ji]

物理意义就是对于某个数 j j j 都可以从比它小的数 j − i j-i ji 的所有拆分方案后再加上 i i i 得到

#include
#define int long long
#define x first
#define y second
using namespace std;

const int mod = 998244353;

void solve()
{
    int n;
    cin >> n;
    vector<int> dp(n + 1);
    dp[0] = 1;
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)
            dp[j] = (dp[j] + dp[j - i]) % mod;
    cout << dp[n] << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

你可能感兴趣的:(比赛总结,c++,算法)