Google APAC 2017 Round E

本文解析了CodeJam竞赛中三道题目的解决方案,包括Diwalilightnings、BeautifulNumbers和PartioningNumber。对于BeautifulNumbers,使用了二分查找优化计算过程;PartioningNumber则运用了线性规划的方法。
摘要由CSDN通过智能技术生成

下午有时间于是又做了下Round E, 算法太弱导致只做了前三道,记录下。

  • A. Diwali lightnings
  • B. Beautiful Numbers
  • C. Partioning Number

A. Diwali lightnings

送分题,循环序列统计B出现了多少次,注意边界条件即可,略过

B. Beautiful Numbers

题目就不贴了,可以去codejam上去看,给定一个数N,求一个进制K,使N在K进制下可以表示为连续的1。
暴力法可以通过小数据,但最大10^19次方显然无法通过。设K进制表示下有m个1,m最大为64(2进制),最小为2(N-1进制)。
N = K^(m-1)+K^(m-2)+…+K+1
当m确定时,N是K的单调递增函数,因此可以用二分法解决,最终时间复杂度是64*O(logN),编程时注意枚举m时,上界是n,可能有溢出的问题,注意。贴下代码,但似乎有些BUG会溢出,欢迎讨论:

#include <iostream>

using namespace std ;
typedef unsigned long long ull ;
ull m, n ;
int T ;

ull work(){
    for(m=64 ;m>=2 ;m--) {
        ull lk = 2;
        ull hk = n-1;
        while (lk<=hk) {
            ull mk = lk/2+hk/2 ;
            if(lk%2==1 && hk%2==1)
                mk++ ;
            ull res = 0 ;
            ull tmp = 1 ;
            bool legal = true ;
            for(int j=0 ;j<m ;j++){
                ull tr = res+tmp ;
                if(tr<res || tr<tmp){
                    //!溢出
                    legal = false ;
                    break ;
                }
                res = tr ;
                ull ttmp = tmp*mk ;
                if(ttmp<tmp || ttmp<mk){
                    legal= false ;
                    break ;
                }
                tmp = ttmp ;
            }
            if(!legal || res>n){
                hk = mk-1 ;
                continue ;
            }
            if(res==n){
                return mk ;
            }
            if(res<n){
                lk = mk+1 ;
                continue ;
            }
        }
    }

}

int main() {
    cin >> T ;
    for(int i=0 ;i<T ;i++) {
        cin >> n ;
        cout << "Case #" << i+1 << ": " ;
        ull res = work() ;
        cout << res << endl ;
    }
}

C. Partioning Number

题意比较长,不再赘述,要求将N个球放到多个桶里面即把数N分割为多个正整数,形成一个序列,要求:

  • 第一个数被D整除
  • 所有的数字相差小于等于2,即只能有三个不同的数字
  • non-decreasing

根据分析我们可以知道第一个数字x只能是D的整倍数,同时后面只能由x+1和x+2,我们设三种不同的数字的个数分别为a,b,c,有多少种满足条件的a,b,c就是结果。
有: a*x+b*(x+1)+c*(x+2) = N
a > 0, b >=0, c>=0
我们可以枚举a,b再加上枚举x共三层循环。大数据会超时
考虑如何降低,我们整理方程:
(a+b+c)*x+b+2c = N
令 p = a+b+c, q = b+2c , 此外 q = N-p*x
由于a>0 则有约束: b+c< p, b+2c=q
这就是一个线性规划问题,求b+2c=q这条直线上有多少个整数点满足b+c< p。很简单的线性规划,注意一些边界条件,例如c是否整数,边界上的点是否满足,b>0, c>0等。假设交点是(b0,c0)几个分界点,b0=0, b0=p等
AC代码

#include <iostream>
using namespace std ;
int T, N, D ;

int work(){
    int ans = 0 ;
    for(int x=D ;x<=N ;x+=D){
        int ma = N/x ;
        for(int p=1 ;p<=ma ;p++) {
            int q = N-p*x ;
            int mb = 2*p-q ;
            if(mb<= 0)
                continue ;
            if(mb>p) {
                if(q>=0) {
                    ans += (q + 1) / 2;
                    if (q % 2 == 0)
                        ans++;
                }
            }
            else {
                ans += mb / 2;
                if (q % 2 == 1 && mb%2==0)
                    ans--;
            }
        }
    }
    return ans ;
}

int main() {
    cin >> T ;
    for (int t=1 ;t<=T ;t++){
        cin >> N >> D ;
        cout << "Case #" << t << ": " ;
        int res = work() ;
        cout << res << endl ;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值