第23次CCF计算机软件能力认证

第一题:数组推导

解题思路:

最大值一定是整个数组的和、最小值一定是set完之后的结果。

n = int(input())
l = list(map(int , input().split()))
print(sum(l))
print(sum(set(l)))

第二题:非零段划分

解题思路:

为数不多的比较难的第二题

考虑p足够大的情况,这时所有的岛都被海水淹没了,只有0个岛屿
然后海平面逐渐下降,岛屿数量开始变化
每当一个凸峰出现,岛屿数就会多一个;
每当一个凹谷出现,原本相邻的两个岛屿就被这个凹谷连在一起了,岛屿数减少一个
可以使用数组cnt[],cnt[i] 表示海平面 下降 到i时,岛屿数量的变化

这样,数组元素cnt[i]中存储的就是该元素被替换为0时,划分数变化的差分值
最大值则只需要从其前缀和(程序中实际为 后缀和)中找出最大值

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int N = 5e5 + 10;
int a[N] , cnt[N];
int n;

int main()
{
    cin >> n;
    for(int i = 1;i <= n;i ++)
        cin >> a[i];
        
    a[0] = a[n + 1] = 0; // 边界没有
    n = unique(a , a + 2 + n) - a; // 去重
    
    for(int i = 1;i < n;i ++)
    {
        int x = a[i - 1] , y = a[i] , z = a[i + 1];
        if(x < y && y > z) // 每当一个凸峰出现,岛屿数就会多一个
            cnt[y] ++;
        else if(x > y && y < z) 
            cnt[y] --;
    }
    
    int res = 0 , sum = 0 , i = 1e4;
    while(i) sum += cnt[i --] , res = max(res , sum);
    cout << res << endl;
    return 0;
}

第三题:脉冲神经网路

解题思路:

模拟

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2010;
const double INF = 1e8;

int n, s, p, T;
double dt;
int h[N], e[N], D[N], ne[N], idx;
double W[N], v[N], u[N], a[N], b[N], c[N], d[N];
int r[N], cnt[N];
double I[1024][N / 2];

static unsigned long _next = 1;

/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    _next = _next * 1103515245 + 12345;
    return((unsigned)(_next/65536) % 32768);
}

void add(int a, int b, double c, int d)
{
    e[idx] = b, W[idx] = c, D[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d%d%d%d", &n, &s, &p, &T);
    scanf("%lf", &dt);
    for (int i = 0; i < n;)
    {
        int rn;
        scanf("%d", &rn);
        double vv, uu, aa, bb, cc, dd;
        scanf("%lf%lf%lf%lf%lf%lf", &vv, &uu, &aa, &bb, &cc, &dd);
        for (int j = 0; j < rn; j ++, i ++ )
        {
            v[i] = vv, u[i] = uu, a[i] = aa, b[i] = bb, c[i] = cc, d[i] = dd;
        }
    }

    for (int i = n; i < n + p; i ++ ) scanf("%d", &r[i]);

    int mod = 0;
    while (s -- )
    {
        int a, b, d;
        double c;
        scanf("%d%d%lf%d", &a, &b, &c, &d);
        add(a, b, c, d);
        mod = max(mod, d + 1);
    }

    for (int i = 0; i < T; i ++ )
    {
        int t = i % mod;
        for (int j = n; j < n + p; j ++ )
            if (r[j] > myrand())
            {
                for (int k = h[j]; ~k; k = ne[k])
                {
                    int x = e[k];
                    I[(t + D[k]) % mod][x] += W[k];
                }
            }

        for (int j = 0; j < n; j ++ )
        {
            double vv = v[j], uu = u[j];
            v[j] = vv + dt * (0.04 * vv * vv + 5 * vv + 140 - uu) + I[t][j];
            u[j] = uu + dt * a[j] * (b[j] * vv - uu);

            if (v[j] >= 30)
            {
                for (int k = h[j]; ~k; k = ne[k])
                {
                    int x = e[k];
                    I[(t + D[k]) % mod][x] += W[k];
                }
                cnt[j] ++ ;
                v[j] = c[j], u[j] += d[j];
            }
        }

        memset(I[t], 0, sizeof I[t]);
    }

    double minv = INF, maxv = -INF;
    int minc = INF, maxc = -INF;

    for (int i = 0; i < n; i ++ )
    {
        minv = min(minv, v[i]);
        maxv = max(maxv, v[i]);
        minc = min(minc, cnt[i]);
        maxc = max(maxc, cnt[i]);
    }

    printf("%.3lf %.3lf\n", minv, maxv);
    printf("%d %d\n", minc, maxc);

    return 0;
}

第四题:收集卡牌

解题思路:

状态压缩,使用 16 位二进制表示所有的情况,当某一位是1的时候表示该牌被选择

然后直接记忆化搜索即可

#include<iostream>

using namespace std;

const int N = 20 , M = (1 << 16) + 10;
int n , k;
double a[N];
double f[100][M]; // 存coin个硬币和st的选择状态下的期望

// coin 硬币个数 , st状态压缩时的某一个时刻的状态 , cnt为state里还没拥有的卡牌数量
double dfs(int coin , int st , int cnt , int dep)
{
    // 记忆化搜索
    if(f[coin][st]) return f[coin][st];
    // 把没有的卡牌全部用硬币换需要的硬币数量小于等于已拥有的硬币数量
    if(cnt * k <= coin) return dep;
    double s = 0;
    for(int i = 0;i < n;i ++)
    {
        // 这张卡牌之前已经获得过了,就会转化为一枚硬币
        if((st >> i) & 1) // 判断第i位是否是1 也就是判断是否选择了这张牌
            s += a[i] * dfs(coin + 1 , st , cnt , dep + 1);
        else
            s += a[i] * dfs(coin , st | (1 << i) , cnt - 1 , dep + 1); // 将i号位的二进制变为1
    }
    f[coin][st] = s;
    return f[coin][st];
}

int main()
{
    cin >> n >> k;
    for(int i = 0;i < n;i ++)
        cin >> a[i];
    
    cout << dfs(0 , 0 , n , 0);
    return 0;
}

第五题:箱根山岳险天下

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

#define x first
#define y second

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 300010;

int n, p, T;

struct Node
{
    int s[2], p, v;
    int rev;
    int sum, mul;
}tr[N];
int stk[N], idx;  // 栈
int fa[N], dep[N];
int A;
vector<PII> level[N];

void pushrev(int x)
{
    swap(tr[x].s[0], tr[x].s[1]);
    tr[x].rev ^= 1;
}

void pushup(int x)
{
    tr[x].sum = tr[x].v;
    int l = tr[x].s[0], r = tr[x].s[1];
    if (l) tr[x].sum = (tr[x].sum + tr[l].sum) % p;
    if (r) tr[x].sum += tr[r].sum;
    tr[x].sum %= p;
}

void pushdown(int x)
{
    if (tr[x].rev)
    {
        pushrev(tr[x].s[0]), pushrev(tr[x].s[1]);
        tr[x].rev = 0;
    }

    auto &root = tr[x], &left = tr[tr[x].s[0]], &right = tr[tr[x].s[1]];
    if (root.mul != 1)
    {
        LL mul = root.mul;
        left.v = left.v * mul % p;
        left.sum = left.sum * mul % p;
        left.mul = left.mul * mul % p;

        right.v = right.v * mul % p;
        right.sum = right.sum * mul % p;
        right.mul = right.mul * mul % p;

        root.mul = 1;
    }
}

bool isroot(int x)  // 判断x是否为原树的根节点
{
    return tr[tr[x].p].s[0] != x && tr[tr[x].p].s[1] != x;
}

void rotate(int x)  // splay的旋转操作
{
    int y = tr[x].p, z = tr[y].p;
    int k = tr[y].s[1] == x;
    if (!isroot(y)) tr[z].s[tr[z].s[1] == y] = x;
    tr[x].p = z;
    tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
    tr[x].s[k ^ 1] = y, tr[y].p = x;
    pushup(y), pushup(x);
}

void splay(int x)  // splay操作
{
    int top = 0, r = x;
    stk[ ++ top] = r;
    while (!isroot(r)) stk[ ++ top] = r = tr[r].p;
    while (top) pushdown(stk[top -- ]);
    while (!isroot(x))
    {
        int y = tr[x].p, z = tr[y].p;
        if (!isroot(y))
            if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
            else rotate(y);
        rotate(x);
    }
}

void access(int x)  // 建立一条从根到x的路径,同时将x变成splay的根节点
{
    int z = x;
    for (int y = 0; x; y = x, x = tr[x].p)
    {
        splay(x);
        tr[x].s[1] = y, pushup(x);
    }
    splay(z);
}

void makeroot(int x)  // 将x变成原树的根节点
{
    access(x);
    pushrev(x);
}

int findroot(int x)  // 找到x所在原树的根节点, 再将原树的根节点旋转到splay的根节点
{
    access(x);
    while (tr[x].s[0]) pushdown(x), x = tr[x].s[0];
    splay(x);
    return x;
}

void split(int x, int y)  // 给x和y之间的路径建立一个splay,其根节点是y
{
    makeroot(x);
    access(y);
}

void link(int x, int y)  // 如果x和y不连通,则加入一条x和y之间的边
{
    makeroot(x);
    if (findroot(y) != x) tr[x].p = y;
}

int find(int x, int y)
{
    int l = 0, r = level[x].size() - 1;
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (level[x][mid].x <= y) l = mid;
        else r = mid - 1;
    }
    return level[x][r].y;
}

int main()
{
    scanf("%d%d%d", &n, &p, &T);

    int cur = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int op;
        scanf("%d", &op);
        if (op == 1)
        {
            int x;
            scanf("%d", &x);
            x ^= A;
            if (x > 0)
            {
                ++ idx;
                tr[idx].sum = tr[idx].v = x;
                tr[idx].mul = 1;
                if (cur) link(cur, idx);
                fa[idx] = cur, dep[idx] = dep[cur] + 1;
                cur = idx;
                level[dep[cur]].push_back({i, cur});
            }
            else
            {
                cur = fa[cur];
            }
        }
        else if (op == 2)
        {
            int s, l, r, y;
            scanf("%d%d%d%d", &s, &l, &r, &y);
            y ^= A;
            l = find(l, s), r = find(r, s);
            split(l, r);
            tr[r].v = tr[r].v * (LL)y % p;
            tr[r].sum = tr[r].sum * (LL)y % p;
            tr[r].mul = tr[r].mul * (LL)y % p;
        }
        else
        {
            int s, l, r;
            scanf("%d%d%d", &s, &l, &r);
            l = find(l, s), r = find(r, s);
            split(l, r);

            printf("%d\n", tr[r].sum);
            if (T) A = tr[r].sum;
        }
    }

    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值