15级算法第一次上机解题报告

这篇博客分享了解决多项式计算、智力问题、数组运算、股票交易、模式寻对和汉诺塔问题的算法思路与代码实现,包括秦九韶算法、动态规划和递归策略的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A 多项式计算器

解题思路:

求多项式的值,很典型的秦九韶算法的应用。如果使用朴素算法,每次算一个,枚举,对于每个i都要进行(i+1)次乘法,复杂度为O(),n在10000范围时显然会超时。可以对算的过程进行复用,记录下每次的,下一次的就由这个得到,复杂度降为O(n)。

更优的方法是秦九韶算法:

改写为:

记=,

取=

则即为多项式的值。

使用秦九韶算法求n次多项式值时,至多进行n次乘法和n次加法,十分高效。

此题中还要注意的是,运算过程中可能会超出int范围,全程long long并且及时取模即可。

代码:

#include <iostream>

using namespace std;

const int MaxT = 10000 + 7;
const long long mod = (int) 1e6 + 7;
int n, x, t, T = 0;
long long ans;
long long a[MaxT];

int main() {
    while (cin >> n >> x >> t) {
        T++;
        cout << "Case #" << T << ":\n";
        while (t--) {
            for (int i = 0; i <= n; i++) cin >> a[i];  //读入系数
            ans = a[n] % mod;
            for (int i = n - 1; i >= 0; i--) {  //秦九韶算法
                ans *= x;
                ans += a[i];
                ans %= mod;
            }
            cout << ans << "\n";
        }
    }
}


 

B 怠惰的园丁王木木

解题思路:

智力题。

由于题目描述中"每点体力可以将一根或者多根草减掉相同的任意高度",因此对于同一种高度的不同的草,都可以看作是同一根。所以,需要减少长度唯一的草的数量。

考虑先将后根草除去的长度,完成后它们的长度分别与前根草的长度相等。这个操作等于是花费1个代价将n根草变成了根草。不断重复这个过程,直到只剩一根草为止。

代码:

#include <iostream>

using namespace std;

long long n, ans;

int main() {
    while (cin >> n) {
        ans = 1;
        while (n > 1) {
            n = n / 2;
            ans++;
        }
        cout << ans << "\n";
    }
}


 

C jhljx学位运算

解题思路:

数组元素不改变,是静态的,因此可以用前缀和处理。假设原数据在nums数组中,用一个额外的数组prefix来存储前缀和,表示, 对于区间,所求答案即为

需要注意,输入的子数组边界不一定是的,如果需要先交换。

代码:

#include <iostream>

using namespace std;

const int MaxN = 1000000 + 7;

int n, k, l, r;
int nums[MaxN], prefix[MaxN];

int main() {
    while (cin >> n) {
        prefix[0] = 0;

        for (int i = 1; i <= n; i++) {
            cin >> nums[i];
            prefix[i] = prefix[i - 1] ^ nums[i]; //前缀和数组
        }
        cin >> k;

        while (k--) {
            cin >> l >> r;
            if (l > r) swap(l, r); //需要特别注意
            cout << (prefix[r] ^ prefix[l - 1]) << "\n";
        }
    }
}


 

D 股票交易

解题思路:

题意即为求出满足的的最大值。

先读入所有元素,然后进行线性遍历。遍历到第i个元素时,记录下之前所有元素的最小值,与当前元素作对比,一方面维护最小值,另一方面更新差值即可。

代码:

#include <iostream>

using namespace std;

const int MaxN = 1000000 + 7;

int n, det, num, Min;

int main() {
    while (cin >> n) {
        det = -1;
        Min = MaxN; //最小值
        while (n--) {
            cin >> num;
            if (num <= Min)
                Min = num; //维护最小值
            else
                det = max(det, num - Min); //更新差值
        }
        if (det < 0) cout << "No solution\n";
        else cout << det << "\n";
    }
}


 

E 模式寻对

解题思路:

求逆序对个数,按理说复杂度为O(tnlogn)的代码很可能会超时,但是似乎没有复杂度更低的办法了,于是就这么做了,居然没有超时。

做法类似归并排序,求区间[l,r]中的逆序对个数,可以利用辅助空间先递归求出[l,mid)和[mid,r]区间里的逆序对个数,并分别完成排序,再从辅助空间回到主空间进行有序归并,归并的同时算出横跨mid两侧的逆序对的个数,三者求和就是总的逆序对个数。

代码:

#include <cstring>
#include <cstdio>

long long inv_pair(int a[], int n) {//统计a数组中前n个元素的逆序对数
    if (n == 1 || n == 0) return 0;
    long long sum = 0;
    int mid = (n / 2);
    sum += inv_pair(a, mid);    //递归求解左半段
    sum += inv_pair(a + mid, n - mid);    //递归求解右半段
    int *b = new int[n + 3];    //归并排序需要辅助空间
    memcpy(b, a, n * sizeof(int));
    for (int i1 = 0, i2 = mid, i = 0; i1 < mid || i2 < n; ++i) {
        if (i2 == n) {
            a[i] = b[i1];
            ++i1;
            sum += i2 - mid;
        } else if (i1 == mid) {
            a[i] = b[i2];
            ++i2;
        } else if (b[i1] <= b[i2]) {
            a[i] = b[i1];
            ++i1;
            sum += i2 - mid;
        } else {
            a[i] = b[i2];
            ++i2;
        }
    }
    delete[] b;
    return sum;
}

const int MaxN = 10000 + 7;
int n, k, l, r;
long long ans;
int nums[MaxN], back[MaxN];

int main() {
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) scanf("%d", &nums[i]);
        scanf("%d", &k);
        while (k--) {
            scanf("%d%d", &l, &r);
            memcpy(back, nums + l, (r - l + 1) * sizeof(int));
            //切记要先复制到辅助空间内,否则影响之后的询问
            ans = inv_pair(back, r - l + 1);
            printf("%lld\n", ans);
        }
    }
}


F 究极汉诺塔

解题思路:

关于汉诺塔有一个性质:从状态A到状态B的最少步数,和从状态B到状态A的最少步数是一样的。

汉诺塔的处理原则是先大后小。先考虑最大的一块盘子,想要搬动最大的一块,需要将这一块上面的盘子和目标位置上的盘子移到辅助柱上,设这些盘子都在辅助柱上的状态称为状态C,整个过程分为3步:

  1. 将当前状态移动至状态C
  2. 将最大的盘子从当前状态移动至目标柱
  3. 将状态C移动至所求的最终状态

 

代码:

#include <cstdio>

const int MaxN = 65;

int n, top, mid;
long long ans;
int pos[MaxN];
int target[MaxN];

inline long long hanoi(int step) {
    return (1LL << (long long) step) - 1;
}

long long move(int stat[], int now, int tgt) {
    if (now == 0) return 0;
    else if (stat[now] == tgt) return move(stat, now - 1, tgt);
    else return move(stat, now - 1, 6 - tgt - stat[now]) + hanoi(now - 1) + 1;
}

int main() {
    while (scanf("%d", &n) != EOF && n) {
        for (int i = 1; i <= n; i++)
            scanf("%d", &pos[i]);
        for (int i = 1; i <= n; i++)
            scanf("%d", &target[i]);
        top = n;
        while (top >= 1 && pos[top] == target[top]) top--;
        if (top > 0) {
            mid = 6 - pos[top] - target[top];
            ans = move(pos, top - 1, mid) + move(target, top - 1, mid) + 1;
        } else ans = 0;
        printf("%lld\n", ans);
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值