2021牛客练习赛90

2021牛客练习赛90

B.寒冬信使

题目链接:https://ac.nowcoder.com/acm/contest/11180/B
在这里插入图片描述
在这里插入图片描述
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int t;
    cin>>t;
    for(int i=1;i<=t;i++)
	{
		string s;
		cin>>s;
		ll sum=0;
		for(int i=0;i<s.length();i++)
		{
			if(s[i]=='1')
			sum+=i+1;
		}
		if(sum%2==1)
		cout<<"T"<<"\n";
		else cout<<"X"<<"\n";
	}
	
    return 0;
}

这道题是个博弈题,一道很有趣的题目。题目的大意是:两个人轮流选一个白色格子并翻转它,以及翻转这个白色格子的前一个格子(翻转指得是变成相反的颜色,即白变黑,黑变白),无法操作者败,如果先手的人一定能胜则输出"T",否则输出"X"。这题的解法我很快就想出来了,但一直犹豫要不要交上去,因为感觉这样写太简单了,而且当时只有80个人写出来了,所以我犹豫了10分钟,等到我试了很多特殊例子后都是正确的我才交了,然后过了。都是题外话,现在说正解。我的想法是如果总共操作了奇数次,那么先手必赢。所以我统计了字符串中每个1的位置,并将它们加起来,如果是奇数,则输出"T",否则输出"X"。那么为什么可以这样写呢?我举个例子吧,首先如果操作时不存在两个1靠在一起的情况,比如0101,从左往右依次把1变成0,那么总共操作了偶数次,2+4=6,那么先手必败(这个应该不用我解释吧,模拟试试就知道了),如果操作时存在两个1靠在一起的的情况,还是拿之前的0101举例子,0101->0110->0000,在这个过程中我们可以发现,两个1靠在一起的情况所贡献的操作次数还是奇数,毕竟i+(i+1)=2*i+1为奇数,加上之前的一次操作总共就是偶数次,于是我们就可以有理由的得出一个结论:位置相加的奇偶数情况决定输出情况。

C.盾与战锤

题目链接:https://ac.nowcoder.com/acm/contest/11180/C
在这里插入图片描述
在这里插入图片描述
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
ll a[N],b[N];
bool compare(ll x,ll y)
{
    return x>y;
}
int main()
{
    ll n,s;
    cin>>n>>s;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    ll ans=0; 
    sort(a+1,a+1+n,compare);
    for(int i=1;i<=2*n;i++)
    {
        b[i]=b[i-1]+a[i];
    }
    for(ll k=1;k<=n;k++)
    {
        ans=0;
        for(ll t=0;;t++)
        {
           ans=max(ans,b[t*k]-t*s);
           if(t*k>=n) break;
        }
        printf("%lld\n",ans);
    }   
    return 0;
}

这道题我一直无法理解这里的子序列的定义。题目中也不给个解释,于是用了错误的理解一直找不出来解决方法。我的理解是相对位置不变,也就是说在原序列5,2,4,1中可以选5,2,4,但不能5,4,2,而答案却是可以的。。。这道题的解法是:对于每个k,枚举破盾数t,范围是0~n/k,但其实破盾数最大可为n/k+1,这就要在枚举中做些小优化了,具体见代码:

 for(ll t=0;;t++)
 {
     ans=max(ans,b[t*k]-t*s);
     if(t*k>=n) break;
 }

然后把前最大的t✖k个数相加减去t✖s,就是当前破盾数所对应的伤害,然后更新最大的伤害即可,我们可以用前缀和预处理前t✖k大的数。这里要注意的是数组的长度和前缀和循环的范围须定义为2倍的n的最大值,即2e6+10,因为如果是1e6+10的话,数组会越界的。那么为什么会越界呢?假设k=n-1,那么按照我们上述写法,当t=2时,2*k就是2✖(n-1)了,所以必须定义为2e6的级别,那么,这就有人回问了,我们别这样判断了,直接这样写不就行了吗:

for(ll t=0;t*k<=n;t++)
 {
     ans=max(ans,b[t*k]-t*s);
 }

你一交就会发现只过了30%的例子,这是为什么呢?其实就是我在上面所说,t是可以为n/k+1的!也就是说我们必须得循环到第一个大于或等于n/k的数才能退出循环。那么为什么可以为n/k+1呢?举个例子你就清楚了,n=10,k=4的时候:
在这里插入图片描述
五角星为破盾时所在的位置,1~10这些数为从大到小排序后的数所在的位置,从这张图我们可以看出有三个破盾点,而不是10/4=2了,最后一个破盾后所选取的范围已经超过了n了,也能解释为什么数组大小为n会越界了。
还有一点要说明的是这种做法的时间复杂度是O(nlogn),证明见下图:
在这里插入图片描述

牛客练习赛142是一场编程竞赛,通常包含多个算法题目,涵盖如数组、字符串、链表、动态规划等常见数据结构算法知识点。针对这类比赛的解题思路和方法,可以从以下几个方面进行分析: ### 题目类型解题策略 1. **数组相关问题** - 常见的题目包括查找数组中出现次数超过一半的数字、寻找缺失的数字、求解最大子数组和等。 - 解题方法包括使用哈希表统计频率、摩尔投票法(适用于多数元素问题)、双指针技巧或前缀和优化。 2. **链表操作** - 链表题目可能涉及反转链表、判断链表是否有环、找出两个链表的相交节点等。 - 例如,在找两个链表相交点的问题中,可以先计算各自长度,然后让长链表先走差值步数,再同步遍历比较节点地址[^3]。 3. **字符串处理** - 包括最长回文子串、无重复字符的最长子串等。 - 可采用滑动窗口、动态规划或中心扩展法等策略。 4. **树图** - 树相关的题目可能涉及二叉树的遍历、路径和、最近公共祖先等问题。 - 图论问题可能需要使用深度优先搜索(DFS)、广度优先搜索(BFS)或拓扑排序等算法。 5. **动态规划** - 动态规划常用于解决背包问题、最长递增子序列、编辑距离等。 - 关键在于定义状态转移方程,并通过迭代或记忆化搜索进行求解。 6. **贪心算法** - 适用于区间调度、活动选择、硬币找零等问题。 - 贪心策略的核心在于每一步都做出局部最优选择。 ### 示例代码:摩尔投票法解决“多数元素”问题 ```python def majorityElement(nums): count = 0 candidate = None for num in nums: if count == 0: candidate = num count += (1 if num == candidate else -1) return candidate ``` 该算法时间复杂度为 O(n),空间复杂度为 O(1),非常适合处理大规模输入的数据集[^2]。 ### 提升解题能力的建议 - **刷题积累经验**:在 LeetCode、Codeforces、AtCoder 等平台上持续练习,熟悉各种题型。 - **学习经典算法**:掌握常见的算法模板,如二分查找、归并排序、快速选择等。 - **阅读官方题解讨论区**:了解不同解法的优劣,尤其是最优解的时间复杂度分析。 - **模拟比赛训练**:定期参加在线编程比赛,提升实战能力和代码调试速度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值