杭电 7007 净化(圈数遍历、思维)

在这里插入图片描述

在这里插入图片描述
这道题我感觉是一道模板题,以后肯定会碰到类似解法的题目。


题目要找每圈的一个固定值,而且还要特判第一圈刚加上来的值。从这两方面考虑的话,寻找第一圈的最大值,但并不限于第一行。
根据题意,如果满足,那么在前两行一定会出现最大值。让 m 减去最大值之后,表示除去第一次走的,剩下的圈数每回的起点就是最大值之后的点,终点在下一行的最大值点。(如果最大值等于走一圈的的值,那么就等同于多减了一圈,所有的圈数的起点相同,第一行可能前半部分 < 0 舍去)
这里还需要特判,我只说一种情况的理解:在第二行增量为 0 时,表示永远不可能走到 m,意思可以理解为对于第一行起,每在走一行,加的量就是一次走的值(从 max 值之后走也一样,因为每一圈走的量相同,总会走满一整圈(这里还可以变一下题目,改为计算最少的走的步子,而不是圈数,方法相同)),所以 x1 == x2 的时候,表示无增量,踢出即可






上面说的是第一种思路
这里再说一下 s = (a+d-1)/d 表示对 a/d 的向上取整,等同于 ceil((double)a/d)

#include<stdio.h>
#include<math.h>

const long long INF = (1LL<<60);
long long a[100010];
int main()
{
	int t;
	scanf("%d",&t);
	for(int i = 1;i <= t;i++)
	{
		long long n,m;
		scanf("%lld %lld",&n,&m);
		long long max = -INF,tmp = 0,x = 0;
		int sign = 0;
		for(int j = 1;j <= n;j++)
		{
			scanf("%lld",&a[j]);
			x += a[j];
		}
		for(int k = 1;k <= 2;k++)
		{
			for(int j = 1;j <= n;j++)
			{
//				if(k == 1){			//问题所在,为什么不能这么写,因为一旦 break 掉,下面的数据就没办法读取 
//					scanf("%lld",&a[j]);
//					x += a[j];
//				}
				tmp += a[j];
				max = tmp > max ? tmp:max;
				if(tmp < 0) tmp = 0;
				if(tmp >= m){
					sign = k;break;
				}
			}
			if(sign) break;
		}
		if(sign){
			printf("%d\n",sign);continue;
		}
		if(x <= 0){
			printf("-1\n");continue;
		}
		printf("%lld\n",(m-max+x-1)/x + 2);
	}
	
	return 0;
}

这是第二种思路:前后两种情况都考虑,前面先减去两行的值,表示减去第一圈中前面为 0 的值,然后在减去最后一圈的 max 值,中间剩下的就是 n 个整行

max 的值不同,x1,x2的含义不同(和上面方法相比)


#include<stdio.h>        //学习前缀和 
#include<stdlib.h>
#include<math.h>
#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
int a[100005];
const long long INF=(1LL<<60);
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        long long m;
        scanf("%d %lld",&n,&m);
        
        long long x1 = 0,x2 = 0;
        long long max1 = -INF;
        int sign = 0;
        long long tmp = 0;
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
            tmp += a[i];
            max1 = max(max1,tmp);
//            max = max < tmp ? tmp:max;
        }
        for(int i = 1;i <= n;i++)
        {
            x1 += a[i];
            if(x1 < 0) x1 = 0;
            if(x1 >= m)
            {
                sign = 1;
                break;
            }
        }
        if(sign)
        {
            printf("1\n");continue;
        }
        x2 = x1;
        for(int i = 1;i <= n;i++)
        {
            x2 += a[i];
            if(x2 < 0) x2 = 0;
            if(x2 >= m)
            {
                sign = 2;
                break;
            }
        }
        if(sign){
            printf("2\n");continue;
        }
        if(x1 == x2){        //当走完q-n,1-q 判断前缀和是否为 0,即是不是走一圈数不增加 
            printf("-1\n");continue;
        }
        
        long long d = x2-x1;
//      前后两种情况都考虑,前面先减去两行的值,表示减去第一圈中前面为 0 的值,
//		然后在减去最后一圈的 max 值,中间剩下的就是 n 个整行 
        printf("%lld\n",(long long)ceil((double)(m-max1-x2)/d)+3);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值