codeforces 675 A~E(Round #353 Div. 2) python and C++ 解法

本文解析了五道ACM竞赛题目,包括简单的数学判断、贪心算法、二叉搜索树的应用、DP+贪心策略等,涉及Python和C++编程实践。

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

PROBLEM LIST

official tutorial

Explanation

I use python in Problem A, B & E.
while in C and D, I found it easier to write in C++.(for I can use STL to reduce my work)

A题:

题意:给出a,b,c,求是否a加若干个c能得到b,是就输出YES,否就输出NO
思路:(b-a)%c==0

a, b, c = map(int, input().split(' '))
if ((a != b and c == 0) or (b > a and c < 0)):
    print("NO")
elif ((a == b) or (b > a and c > 0 and ((b - a) % c == 0)) or (a > b and c < 0 and ((a - b) % c == 0))):
    print("YES")
else:
    print("NO")

B题:

题意:给出a,b,c,求是否a加若干个c能得到b,是就输出YES,否就输出NO
思路:(b-a)%c==0

a, b, c = map(int, input().split(' '))
if ((a != b and c == 0) or (b > a and c < 0)):
    print("NO")
elif ((a == b) or (b > a and c > 0 and ((b - a) % c == 0)) or (a > b and c < 0 and ((a - b) % c == 0))):
    print("YES")
else:
    print("NO")

C题:

按和为0切段,答案是(总数-段数),一共有mp[sum]段可以使和为0
次数最少,就要使段数最多–>>贪心算法+排序+map+数据结构
注意,所有点相当于一个环,就是说可以首尾相连

#include <iostream>
#include <algorithm>
#include <map>
using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    map<long long,int>mp;
    long long sum=0;
    int ans=n-1,aa;//初始化只有一段 
    for (int i=0;i<n;i++)
    {
        scanf("%d",&aa);
        sum+=aa;
        mp[sum]++; //分成mp[sum]个部分
        ans=min(ans,n-mp[sum]);
    }
    printf ("%d\n",ans);
    return 0;
}

D题:

binary search tree
题意: 二叉搜索树,不用旋转,强插,输出除了第一个点之外所有点的父亲值
可以用C++的STL的set和map来当平衡树。

#include<iostream>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
/*
binary search tree

题意: 二叉搜索树,不用旋转,强插,输出除了第一个点之外所有点的父亲值
可以用C++的STL的set来当平衡树。

思路:找到v,把之前插入的放入set,用upper_bound找到l<v<r
*/

int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);

    set<int>numbers;
    map<int, int>left;
    map<int, int>right;
    int n, v;
    cin >> n >> v;
    numbers.insert(v);
    for (int i = 0; i < n - 1; i++)
    {
        cin >> v;
        auto it = numbers.upper_bound(v);
        int res;
        if (it != numbers.end() && left.count(*it) == 0)
        {//upper_bound的结果没在最后也没有左子树,就是插入这里
            left[*it] = v;
            res = *it;
        }
        else
        {
            it--;
            right[*it] = v;
            res = *it;
        }
        numbers.insert(v);
        cout << res;
        if (i != n - 2)
            cout << ' ';
    }
    return 0;
}

E题:

题意:

有一个一条直线的地铁线路。给出a数组,在每个站点i只能买到去往[i+1, a[i]]内的票。设p(i,j)为从i到j所需要的最少票数,求对所有ij的p(i,j)的和。(1<=i< j<=n)

思路:

dp[i] 是从i到后面所有站点的最小票数和
当从一个站点i到不了所有点时,会到它能到的点中a[i]最大的点x。这时就能用到dp[x]。
其中自己能走i+1~x-1点,用x-i票
x能到x+1~n,用b[x]票
x能走的那些中,x+1 ~ a[i]是i自己能走的,把x走的当做自己走的,更远的要自己买票走到x,要n - a[i]张票
得到:dp[i] = x-i + dp[x] + n - a[i]
x能走的肯定比a[i]远,因为a[a[i]]肯定要大于a[i]
这样,我们要做的就是每次找出区间[i+1, a[i]]中a[x]最大的x
这可以用各种RMQ方法,不能用单调区间O(1)求,因为这个区间不是纯粹向左移动的,左界是一个个往左,右界是会来回动的。
所以可以维护一个只进不出的单调下降队列,然后用二分找。O(nlogn)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2017/3/22 15:35
# @Author  : mazicwong
# @File    : 675E DP+greedy.py

'''
英文: buy only tickets to stations from i+1 to ai inclusive (inclusive 表示包含在这个路段内的)

give:
n
a1,a2...an-1

使用:deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
debug用法说明:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431953239820157155d21c494e5786fce303f3018c86000
'''

from collections import deque


def argmax(que, z): #二分求[i+1, a[i]]中a[x]最大的x
    l = 0
    r = len(que) - 1
    while (l <= r):
        mid = int((l + r) / 2)
        x = que[mid]['i']
        if (x <= z):
            r = mid - 1
        else:
            l = mid + 1
    return que[l]['i']


def solve(n, A):
    a = [0] * (n + 1)
    a[1:] = A
    dp = [0] * (n + 1)
    dp[n - 1] = 1
    que = deque()
    que.append({'i': n - 1, 'a': a[n - 1]})
    for i in range(n - 2, 0, -1):
        if (a[i] >= n):
            dp[i] = n - i
        else:
            x = argmax(que, a[i])
            dp[i] = x - i + dp[x] + n - a[i]
        while (len(que) > 0 and que[-1]['a'] < a[i]):
            que.pop()
        que.append({'i': i, 'a': a[i]})
    return sum(dp)


n = int(input())
a = map(int, input().split(' '))
print(solve(n, a))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值