pongo题解之覆盖数字

几天没上pongo,昨晚回宿舍前看到出新题了,没时间做,今天上午过来开始做,中间各种杂事,断断续续想了几次,大概在吃午饭前想到了思路,吃饭回来写了下,提交却遇到pongo编译程序崩溃,过了半下午终于可以编译了~^..^~,下面首先是题目内容:

题目详情

给定整数区间[a,b]和整数区间[x,y],你可以使用任意多次a,b之间的整数做加法,可以凑出多少个[x,y]区间内的整数?

输入 a,b,x,y,其中1<= a < b <= 1000000000,  1 <= x < y <= 1000000000。

输出: 用[a,b]内的整数做任意多次加法,可以得到多少个[x,y]内的整数。


例如a = 8, b = 10, x = 3 , y = 20

我们可以得到 [3..20]之间的整数 8, 9, 10, 16 ( 8 + 8), 17(8 + 9), 18(9 + 9), 19(9 + 10), 20(10 + 10),因此输出8。


问:2+3=5 1+4=5 这算1个还是2个?

答:算1次 问你能覆盖多少个不同的数字 [x,y]全覆盖住得话 就是y - x + 1

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

a,b,x,y的范围很大所以不可能去遍历,开始想了几种方法都不理想,下面我还是以a = 8, b = 10, x = 3 , y = 20来讲解我的思路,

先不考虑x,y,只考虑a,b能能覆盖的整数:

n=1   [a*n,b*n] = [8,10] 完全覆盖

n=2   [a*n,b*n] = [16,20] 完全覆盖

n=3   [a*n,b*n] = [24,30] 完全覆盖

n=4   [a*n,b*n] = [32,40] 完全覆盖

n=5   [a*n,b*n] = [40,50] 完全覆盖

n=6   [a*n,b*n] = [48,60] 完全覆盖

....

发现从n=5开始,本次区间与上一次的区间开始有重合,导致后面所有的数均能覆盖,可以看出上例中的连续覆盖的起点是32,它可以这样求得

8n<=10(n-1) --->  n>=5 -->从n=5处开始和上次的区间有重叠,从n=4处开始连续覆盖。

然而此例中连续覆盖起始值为32,比20要大,所以只需计算下连续覆盖起始之前的几个区间能覆盖多少个数字即可

化为一般情况求开始有重合处n:

a*n<=b*(n-1)

n>=b/(b-a)

即n=b/(b-a)对上取整

那么连续覆盖的起始点为z= (n-1)*a

当z<=x时,能覆盖[x,y]区间

当x<z<=y时,能覆盖[z,y],并且需要判断1到n-2倍那些区间能覆盖哪些数,

当z>y时,需要判断1到n-2倍那些区间能覆盖哪些,

下面是代码:

#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
class Test {
public:
    static int howmany (int   a,int   b,int   x,int   y){
        int res=0;
        int n=b/(b-a);
        if(0!=b%(b-a))
            ++n;
        long long z=a*(n-1);//
        //std::cout<<z<<std::endl;
        if(z<=x){
            res=y-x+1;
        }else if(z<=y){
            res+=y-z+1;
            y=z-1;
            for(int i=n-2;i>=1;--i){
                int tmpUp=i*b;        
                if(tmpUp<x)
                    break;
                int tmpDown=i*a;
                if(tmpDown<x){
                    res+=tmpUp-x+1;
                }else{
                    res+=tmpUp-tmpDown+1;
                }//else
            }//for
        }else{//z>y
            for(int i=n-2;i>=1;--i){
                int tmpUp=i*b;        
                if(tmpUp<x)
                    break;
                int tmpDown=i*a;
                if(tmpDown>y)
                    continue;
                tmpUp=tmpUp>y?y:tmpUp;
                tmpDown=tmpDown<x?x:tmpDown;
                res+=tmpUp-tmpDown+1;
            }//for
        }//else
        return res;
    }
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
    cout<<Test::howmany(8,10,3,20)<<endl;   
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。

上面代码通过了pongo,但是代码存在溢出的风险,并且可以对循环检测1至n-2倍处做些优化,如下:

#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
class Test {
public:
    static int howmany (int a,int b,int x,int y){
        int res=0;
		long long tmpA=a,tmpB=b;//乘法中,位数向高位数转换,int-->double
		int n=b/(b-a);
		if(0!=b%(b-a))
			++n;
		long long z=tmpA*(n-1);//
		if(z<=x){
			res=y-x+1;
		}else if(z<=y){
			res+=y-z+1;
		//	y=z-1;
			for(int i=n-2;i>=1;--i){
				long long tmpUp=i*tmpB;		
				if(tmpUp<x)
					break;
				long long tmpDown=i*tmpA;
				if(tmpDown<x){
					res+=tmpUp-x+1;
				}else{
					res+=tmpUp-tmpDown+1;
				}//else
			}//for
		}else{//z>y
			int k=y/tmpA;
			if(k>n-2)
				k=n-2;
			for(int i=k;i>=1;--i){
				long long tmpUp=i*tmpB;		
				if(tmpUp<x)
					break;
				long long tmpDown=i*tmpA;			
				if(tmpDown>y)
					continue;
				tmpUp=tmpUp>y?y:tmpUp;
				tmpDown=tmpDown<x?x:tmpDown;
				res+=tmpUp-tmpDown+1;
			}//for
		}//else
		return res;
    }
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
    cout<<Test::howmany(99999999,100000000,100000001,1000000000)<<endl;   
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。

此代码也通过了pongo


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值