NUEQ-ACM2022预备队第一次双周赛

第一次双周赛

第一题

题目1

n个网格的土壤土地排成一行,从1到n,其中一些是百合花。在网格上放一些猫粮,但对于任何有猫粮的网格i,在区域[i−1, i+1]不得含有百合花。最大限度地增加有猫粮的格子。

思路1

有猫粮的格子最多时,第一个土壤必须放猫粮。接着逐个先后移动,若能放猫粮就放猫粮,直到结束。此时则为答案。

代码1

#include<iostream>
using namespace std;

//存放是否有百合花
bool grid[1001];
int n;

int main()
{
    //输入
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        char g;
        cin >> g;
        if (g == 'L')
        {
            grid[i] = true;
        }
        else
        {
            grid[i] = false;
        }
    }
    //边缘两个网格设置为无百合花,便于遍历
    grid[0] = grid[n + 1] = false;
    
    //答案
    string ans = "";
    for (int i = 1; i <= n; i++)
    {
        //保证附近无百合
        if (!grid[i - 1] && !grid[i] && !grid[i + 1])
        {
            ans += "C";
        }
        //该网格是百合
        else if (grid[i])
        {
            ans += "L";
        }
        //该网格附近有百合
        else
        {
            ans += ".";
        }
    }
    cout << ans << endl;
    return 0;
}

第二题

题目2

求16进制数的高精乘法

思路2

与10进制的高精度乘法类似,只需要把进位时的10改为16,再添加16进制与10进制转换,即可实现

代码2

#include<iostream>
using namespace std;

//最大可能的位数
const int Max = 10000;
//第一个高精数,第二个高精数,结果高精数
int high1[Max], high2[Max], result[Max];
//第一个高精数长度,第二个高精数长度,结果高精数长度
int length1, length2, lengthr;

//相乘函数
void multiply()
{
    //直接相乘
    for (int i = 0; i < length1; i++)
    {
        for (int j = 0; j < length2; j++)
        {
            result[i + j] += high1[i] * high2[j];
        }
    }
    //进位
    for (int i = 0; i < Max - 1; i++)
    {
        result[i + 1] += result[i] / 16;
        result[i] %= 16;
    }
    //计算位数
    for (int i = Max - 1; i >= 0; i--)
    {
        if (result[i] != 0)
        {
            lengthr = i + 1;
            return;
        }
    }
}

//将16进制数字字符串逐字符转换为10进制(以数组形式)
void HexToDec(string hex, int* high)
{
    for (int i =  hex.size() - 1; i >= 0; i--)
    {
        if (hex[i] >= '0' && hex[i] <= '9')
        {
            high[hex.size() - 1 - i] = hex[i] - '0';
        }
        else if (hex[i] >= 'A' && hex[i] <= 'F')
        {
            high[hex.size() - 1 - i] = hex[i] - 'A' + 10;
        }
    }
}

//将结果数组转换为16进制数字字符串
string DecToHex()
{
    string s = "";
    for (int i = lengthr - 1; i >= 0; i--)
    {
        if (result[i] >= 0 && result[i] <= 9)
        {
            s += result[i] + '0';
        }
        else if (result[i] >= 10 && result[i] <= 15)
        {
            s += result[i] - 10 + 'A';
        }
    }
    return s;
}

int main()
{
    //输入和转换
    string input;
    cin >> input;
    HexToDec(input, high1);
    length1 = input.size();
    cin >> input;
    HexToDec(input, high2);
    length2 = input.size();
    //线程
    multiply();
    //输出
    if(lengthr == 0)
    {
        cout << 0 << endl;
        return 0;
    }
    cout << DecToHex() << endl;
    return 0;
}

第三题

题目3

有n个敌人在某处以1m/s速度接近,小明有射程为m的枪,且枪需要换弹,问小明最长的换弹时间。

思路3

该题叫容易判断答案是否正确,并且答案的范围极大,可以使用二分答案。
判断答案是否正确的思路:模拟时间的推进,若有敌人到达了小明则不正确。

代码3

#include<cstdio>
#include<algorithm>
using namespace std;

int enemy[100000];
int n, m;

//快读(不需要)
inline void read(int &x)
{
    int s = 0, w = 1; char ch = getchar();
   while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar(); }
   while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   x = s*w;
}

//判断函数
bool check(int speed)
{
    //过去的时间
    int time = 0;
    //如果最近的敌人还在射程外,则快进时间到敌人进入射程
    time += max(0, enemy[0] - m);
    //循环模拟时间流逝(以一个个敌人为单位,一次循环处理一个敌人)
    for (int i = 1; i < n; i++)
    {
        //换弹时间
        time += speed;
        //如果敌人已经到了,就是无效答案
        if (enemy[i] < time)
        {
            return false;
        }
        //如果敌人在射程外,快进时间到敌人进入射程
        time += max(0, enemy[i] - time - m);
    }
    //如果处理完了,就说明答案有效
    return true;
}

int main()
{
    //读取
    read(n);
    read(m);
    for (int i = 0; i < n; i++)
    {
        read(enemy[i]);
    }
    //排序,保证近的敌人在前,方便检查
    sort(enemy, enemy + n);
    //左边界、有边界、答案
    int l = 1, r = enemy[n - 1] / n, ans;
    //二分
    while (l <= r)
    {
        //中间值
        int mid = (l + r) / 2;
        //判断,若有效则更新答案,并继续判断右边界
        if (check(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        //无效则在左边界判断
        else
        {
            r = mid - 1;
        }
    }
    printf("%d", ans);
    return 0;
}

第四题

题目4

给一个链表,和一个数k,要求逐k个将链表的节点反转

思路4

写一个结构体,存储数据和下一个节点。用一个数组存链表(索引为地址)。再用一个新数组存反转后的链表,计算得到下一个节点的地址。最后再逐个输出新数组。

代码4

#include<iostream>
#include<cstdio>
using namespace std;
//节点结构体
struct node
{
    int next;
    int item;
};
//原链表
node list[100005];
//由链表头开始先后遍历的索引得到地址
int indexToAddress[100005];
//新链表
node newlist[100005];
int main()
{
    //首地址
    int firstAddress;
    //临时存储地址,n,k,临时存储数据,临时存储下一个地址
    int address, n, k, item, next;
    cin >> firstAddress >> n >> k;
    //建立链表
    for (int i = 1; i <= n; i++)
    {
        cin >> address >> item >> next;
        node n;
        n.item = item;
        n.next = next;
        list[address] = n;
    }

    //从链表头开始遍历整个链表,以获得真正的链表中节点的个数,处理有给出的节点不在链表上的情况
    address = firstAddress;
    n = 0;
    int i = 1;
    while (address != -1)
    {
        indexToAddress[i] = address;
        address = list[address].next;
        i++;
        n++;
    }

    //需要反转的次数
    int reverse = n / k;
    //第k次反转的首节点地址
    firstAddress = indexToAddress[k];
    //反转
    for (int i = 0; i < reverse; i++)
    {
        //除该次反转的首节点(首节点的下一个节点在下一次反转的节点中,单独处理)
        for (int j = 0; j < k - 1; j++)
        {
            //当前节点在反转后对应的序号
            int index = i * k + k - j;
            //新节点
            node newNode;
            newNode.item = list[indexToAddress[index]].item;
            //当前节点在反转后的下一个节点是反转前前一个节点
            newNode.next = indexToAddress[index - 1];
            //添加至新链表
            newlist[indexToAddress[index]] = newNode;
        }
        //处理该次反转的首节点
        node newNode;
        newNode.item = list[indexToAddress[i * k + 1]].item;
        //如果还有下一次反转,则该次反转后的这一段的末节点是下一次反转在反转前的末节点
        if (i * k + 2 * k <= n)
        {
            newNode.next = indexToAddress[i * k + 2 * k];
        }
        //否则就是反转前末节点的下一个节点
        else
        {
            newNode.next = list[indexToAddress[i * k + k]].next;
        }
        //添加至新链表
        newlist[indexToAddress[i * k + 1]] = newNode;
    }

    //不需要反转的部分
    for (int i = reverse * k + 1; i < n + 1; i++)
    {
        newlist[indexToAddress[i]] = list[indexToAddress[i]];
    }

    //遍历输出
    address = firstAddress;
    while (address != -1)
    {
        if (newlist[address].next == -1)
        {
            printf("%05d %d %d\n", address, newlist[address].item, newlist[address].next);
        }
        else
        {
            printf("%05d %d %05d\n", address, newlist[address].item, newlist[address].next);
        }
        address = newlist[address].next;
    }

    return 0;
}

第五题

题目5

求一个一元三次方程在一定范围内的解

思路5

设该一元三次方程的导函数的零点为a, b,该一元三次方程的三个根分别在(-∞, a),(a, b),(b, +∞)之间。则可以先求导函数的零点,在分三块分别二分查找零点位置。

代码5

#include<iostream>
#include<iomanip>
using namespace std;

//导函数两个零点(两个极值)
double m1, m2;
//给定系数
double a, b, c, d;
//给定范围
double p, q;
//导函数
double fd(double x)
{
    return 3 * a * x * x + 2 * b * x + c;
}
//原函数
double f(double x)
{
    return a * x * x * x + b * x * x + c * x + d;
}

//求零点(二分)(参数:函数,左边界,右边界)
double getZeroPoint(double (* f)(double), double l, double r)
{
    //二分
    while (l <= r)
    {
        //中间值
        double mid = (l + r) / 2;
        //求函数值
        double y = f(mid);
        //精度要求为1e-6,所以范围要小于1e-6
        if (-1e-8 < y && y < 1e-8)
        {
            return mid;
        }
        //零点在左侧
        else if (y * f(l) <= 0)
        {
            r = mid;
        }
        //零点在右侧
        else
        {
            l = mid;
        }
    }
}

//求极值(导函数的零点)
void getMaxAndMin()
{
    double mid = - (2 * b) / (3 * a) /2;
    m1 = getZeroPoint(&fd, p, mid);
    m2 = getZeroPoint(&fd, mid, q);
}

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a >> b >> c >> d >> p >> q;
        getMaxAndMin();
        cout << setiosflags(ios::fixed) << setprecision(6) << getZeroPoint(&f, p, m1) << " " << getZeroPoint(&f, m1 ,m2) << " " << getZeroPoint(&f, m2, q) << " " << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值