谷仓

谷仓

编程社四连测

 

目录

目录

谷仓

编程社四连测

目录

题目

样例输入

样例输出

解析

代码(一)

代码(二)


题目

题目描述

有一个圆形的谷仓,共有n个房间,按顺时针编号从1到n。现在有许多头奶牛,他们都有自己最喜欢的一个房间。傍晚回家时,奶牛们去找自己最喜欢的房间。如果发现被占了,他们就会按照顺时针方向找第一个空闲的房间住进去。现在请你输出最小的空闲的房间号。注意,这个答案和奶牛们回家的顺序是无关的。

2<=n<=3000000,1<=k<=10000,A,B的值在区间[0,10^9]。

输入

输入格式:

第一行两个整数n,k。

接下来有k行。每行4个整数,x,y,a,b.表示有x头奶牛喜欢f[1],f[2]……,f[y]。其中f[i]=(a*i+b)%n+1.

输出

输出最小的空闲房间号。

样例输入

10 3
3 2 2 4
2 1 0 1
1 1 1 7

样例输出

6

解析

此题其实我们可以使用暴搜的思想解决,我们首先循环一遍,将有奶牛喜欢的房间都放一头奶牛,我们以样例为例:

12345678910
0200004030

我们首先把数据读进了 a 数组里,然而我们还需要一个变量  sum ,存放后还剩的奶牛个数

isuma[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a[10]
100200004030
210100004030
300110004030
400110004030
500110004030
600110004030
730110001030
820110001130
940110001110
1030110001111

 

最后我们发现只剩下 3 只奶牛未能进仓,所以我们还需要二次遍历,仍然是从头开始,将剩余牛一次放入未有奶牛的谷仓(因为最后几个位置一定是被占满的,否则就不会多出奶牛,奶牛要么是前面谷仓多出的牛,它们有仓就进最后未能进谷仓的一定是多出的奶牛,其次就是最后一个谷仓的奶牛,它们从下一个位置顺时针遍历就是从 1 号元素开始)

isuma[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a[10]
030110001111
121110001111
221110001111
321110001111
411111001111
501111101111
601111101111
701111101111
801111101111
901111101111
1001111101111

 

至此,大概的算法已经明白,所以就直接看代码吧

代码(一)

#include<cstdio>
#define M 3000000 + 5
#define LL long long
#define reg register

LL sum,n,k;
LL a[M];

int main(){
    scanf("%lld%lld",&n,&k);
    for (reg int i = 1;i <= k; ++ i){
        int x,y;
        LL a1,b;
        scanf("%d%d%lld%lld",&x,&y,&a1,&b);
        for (reg int j = 1;j <= y; ++ j){
            int s = (a1 * j + b) % n + 1;
            a[s] += x;
        }
    }
    for (reg int i = 1;i <= n; ++ i){
        if (sum || a[i]){
            sum += (a[i] - 1);
            a[i] = 1;
        }
    }
    int i = 1;
    while (i <= n){
        if (sum || a[i]){
            sum += a[i] - 1;
            a[i] = 1;
        }
        if ( ! sum && ! a[i]){
            printf("%d\n",i);
            return 0;
        }
        ++ i;
    }
    return 0;
}

想必这里大家会有个疑惑吧

if (sum || a[i]){
     sum += (a[i] - 1);
     a[i] = 1;
}

其实这里很好明白,再次运用我们分类讨论的方法:

  1. sum != 0 && a[i] != 0,我们就可以把a[i] - 1 只奶牛放入 sum 里
  2. sum == 0 && a[i] != 0 ,同上
  3. sum != 0 && a[i] == 0,因为这里是 sum += (a[i] - 1);,所以加一个负数就等于减去那个数的相反数,如果 a[i] == 1 的话,其实就相当于不对他就行操作
  4. sum == 0 && a[i] == 0,完全进不去,既然没有多余的牛,谷仓里也没有牛,我们操作干嘛呢

但是这份代码,大家尽量不要尝试,原因有下:

  1. (错得有点多)

  2. 我们再对比一下,即将给出代码的时间(一),和这份代码的时间(二)

一)

二)

代码(二)

其实就是将第一个代码中的 while 变为 for 循环,然后再用一个 for 循环找答案 (这个竟然要快一些,可能是因为 for 比 while 节时一些吧)

#include<cstdio>
#define M 3000000 + 5
#define LL long long
#define reg register
 
LL sum,n,k;
LL a[M];
 
int main(){
    scanf("%lld%lld",&n,&k);
    for (reg int i = 1;i <= k; ++ i){
        int x,y;
        LL a1,b;
        scanf("%d%d%lld%lld",&x,&y,&a1,&b);
        for (reg int j = 1;j <= y; ++ j){
            int s = (a1 * j + b) % n + 1;
            a[s] += x;
        }
    }
    for (reg int i = 1;i <= n; ++ i){
        if (sum || a[i]){
            sum += (a[i] - 1);
            a[i] = 1;
        }
    }
    for (reg int i = 1;i <= n; ++ i){
        if (sum || a[i]){
            sum += (a[i] - 1);
            a[i] = 1;
        }
    }
    for (reg int i = 1;i <= n; ++ i){
        if ( ! a[i]){
            printf("%d",i);
            return 0;
        }
    }
}

 

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值