Educational Codeforces Round 158 (Rated for Div. 2)补题

Line Trip

题目大意:一条水平的路,从0开始出发,有n个加油站,我们走到x在回到0点,请问油箱的最大容量是多少。只有在加油站才可以加油。

思路:可以看到路被划分成若干段,每一段之间是不能加油的,所以要找出每段差值的最大值,另外,x到最后一个加油站之间的路应该被算两次。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,x;
        scanf("%d%d",&n,&x);
        int mx=0,c=0,d;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&d);
            mx=max(mx,d-c);
            c=d;
        }
        if(d<x) mx=max(mx,2*x-2*d);
        printf("%d\n",mx);
    }
}

Chip and Ribbon

题目大意:我们有一个芯片和一个数轴,芯片最开始放在位置1,可以对它进行两种操作:

1.前进一格

2.跳到任意位置(包括本身位置)

我们它每次覆盖过的位置都会加1,我们已经得到了结果序列,现在要求最少进行多少次操作2可以得到结果序列。

输入:

4

4

1 2 2 1

5

1 0 1 0 1

5

5 4 3 2 1

1

12

输出:

1

2

4

11

思路:我最开始想的是一遍扫过去,所有的位置都减1,然后去找两个0之间的数,两个0之间的数的2操作等于这段区间的最大值,但是这样有很明显的bug,两0之间的数如果是4 3 2 1 2或者是4 3 2 2 3,那么操作将变得很麻烦。转变思路,为什么要跳,因为前一个数小于后一个数,从前往后移的时候前一个数凑齐了,后一个数就不够,那么就得在后一个数的位置跳d=a[i]-a[i-1]次,假设只有3,5两个数,我们先不管3是怎么凑齐的,但是我们可以知道,3凑齐的时候,5也必然为3,那么现在需要在5的位置跳5-3=2次。现在就考虑3怎么凑齐的,是由上一个转移来的,但是上衣给没有,所以实际上就考虑除了刚开始放在这儿产生的1次外,还需要跳3-1=2次。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[200010];
signed main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
		}
		int ans=a[1]-1;
		for(int i=2;i<=n;i++)
		{
			if(a[i]>a[i-1])
			ans+=a[i]-a[i-1];
		}
		printf("%lld\n",ans);
	}
} 

Add, Divide and Floor

题目大意:给定n个元素,每次选一个数x,对于每个ai执行以下操作:(ai+x)/2下取整,问最少操作多少次可以使所有的ai都相等,另外,如果操作次数小于x则打印每次选的数x,否则输出最小次数即可。

思路:仔细想想这个题,我们假定有ai和aj两个元素,且ai<aj,那么(ai+aj)/2不就相当于取了中点,一直这样操作可以一点一点逼近ai,也就是二分更新的思想。所以为了使操作次数最少,我们每次都将x设定为原数组中最小的那个数。所以最重要的是要弄清楚这个操作的实际含义是什么。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200010];
signed main()
{
    int t;
    scanf("%lld",&t);
    while(t--)
    {
        int n;
        scanf("%lld",&n);
        int mi=1e9+7;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            mi=min(mi,a[i]);
        }
        int mx=0;
        for(int i=1;i<=n;i++)
        {
            int k=0;
            while(a[i]!=mi)
            {
                k++;
                a[i]=(a[i]+mi)/2;
            }
            mx=max(mx,k);
        }
        if(mx<=n)
        {
            printf("%lld\n",mx);
            for(int i=1;i<=mx;i++) printf("%lld ",mi);
            if(mx) printf("\n");
        }
        else printf("%lld\n",mx);
    }
}

Yet Another Monster Fight

题目大意:一个攻击是这么定义的:选中一个角色,对它进行强度为x的攻击,然后攻击可以扩散开,不过每次只能左右二选一进行扩散,每次扩散的强度是递减的,问最优策略的最坏情况是初始攻击为多少

input

6
2 1 5 6 4 3

output

8

input

5
4 4 4 4 4

output

8

input

2
1 1000000000

output

1000000000

思路:很显然,对于每个点来说,从开头或者结尾到它是最长的,但是我们要找所有策略中最坏情况的最少次数,翻译成人话就是,每选一个位置都是一种策略,在这种策略中,将这种策略的最大攻击视为这种策略的攻击值,然后在所有策略中找到最优的那个。

我们任意挑一个位置i,可以发现i前面的要想使次数最多要先往后延伸,然后到终点了再往前延伸,那么对初值的要求就是不小于a[j]+n-j;i后面的显然就是要从i开始,先往前延伸,延伸到头了再往后延伸,那么对初值的要求就是不小于a[j]+j-1,i处的初值就是a[i],我们要在这种策略的每一个中找一个最大的,然后作为选i的次数。从每一个数的次数中选出最小的。

因为我们可以定初始选哪个以及攻击的初值,但是每次会往哪边延伸我们不知道,所以就要讨论出最坏的情况。

n很大,肯定需要进行预处理,那么我们该如何进行预处理呢。我们仔细观察每个值,需要用到的就是它前面的最大的(前缀),它后面最大的(后缀),以及它本身的值。这里之所以提到前后缀是因为前后缀是可以进行状态累计的,那么我们只要开三个数组来存一下每个i的这些值,就得到了选这个i时的最少次数。

预处理的时候,对于每个值,记录从开头到它和从结尾到它的次数,前缀后缀计算就在统计完后进行正逆两种顺序的访问,然后进行更新即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[300010],pr[300010],la[300010];
signed main()
{
	int n;
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	int mx1=0,mx2=0;
	for(int i=1;i<=n;i++)
		pr[i]=a[i]+i-1,la[i]=a[i]+n-i;
	
	//pr[i]表示从i到n,区间内的这些全都到开头后再转移的最大值	
	//i后面的要从前面来
	int c=0;
	for(int i=n;i>=1;i--)
		c=max(c,pr[i]),pr[i]=c;//要统计的是i前面的
		
	//la[i]表示从1到i,区间内的这些全都到结尾后再转移的最大值	
	c=0;	
	for(int i=1;i<=n;i++)
		c=max(c,la[i]),la[i]=c;//要统计的是i前面的
		
	int ans=2e9+7;	
	for(int i=1;i<=n;i++)
	{
		ans = min(ans,max(a[i],max(pr[i+1],la[i-1])));//选i,看前后
	}
	printf("%lld\n",ans);
}

反思:遇到最大的问题在于看到题目时产生的畏难情绪,以及一旦认定思路尝试不通就不愿再换别的思路,以及思维上需要很大的提升。

复盘:

状况:这次比赛出现严重失误,而且不是这一场,上一场也出现严重失误,div2竟然只写出一题,而且是在第二题不是很难的情况下,而且一碰到难题,第一反应是退缩。

深层剖析:最近一直在abc中写题,题目在自己能力范围之内而且没有拔高,试图通过更快的出AB来提升分数,但是最近写的题难点在于细节的处理而非思维,最近几场比赛被卡住的是思维。所以问题在于思维的提升以及面对难题逼迫自己去想的态度。

前段时间上分的分析:那时候出了比赛以外,写的题目难度都在1200-1400,同时基本都是自己写的,没有看题解,写完立即复盘思路,对思维有明显的锻炼效果。同时打完一场比赛后,也会以及写题解复盘思路。

后期练习:补题,一方面是赛后补题,一方面去补之前的比赛,div2三题,div3除了最后一题,div4ak。

询问他人:赛后补题,把自己卡壳的题补了,再去往后补一题,遇到新的知识点了就去学习,再找相应的题目练手

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值