Codeforces 625D ---- Finals in arithmetic

来源:互联网 发布:关于编程的书 编辑:程序博客网 时间:2024/04/28 09:35

题目:CODEFORCES PROBLEMSET 625D Finals in arithmetic

所属比赛:Codeforces Round #342 (Div. 2)

        前言:纯天然思路代码,如果有什么不足和缺陷,请务必留言指正!

        题意:给出一个n(n是不超过100000位的大于0的十进制整数),找到一个a,使a和flip(a)等于n,其中flip(a)是将a的按照每一位数字翻转得到的数例如a = 13,flip(a) = 31,a = 120 ,flip(a) = 21,若找不到满足条件的a,则输出0,a不能含有前导零。

        思路:很明显这是一道构造题,如果没有进位,那么根据n的每一位数字,就可以构造出a的每一位数字,所以,最重要的是找到a + flip(a) 的计算过程中的进位.如图所示:

  

这里设置的carry数组,其中carry[i]表示,a + flip(a) 的计算中,第i位是否有来自i+1位的进位,我设置的i是由前到后递增。

确定carry数组后,根据n的第i位数字,carry[i]和carry[i-1],就可以决定第i位所加的两个数字的和是多少,从而构造出a,当然a不唯一。

这道题就变成了怎么确定carry数组,思路比较简单,但是细节非常多,稍有不慎就会漏掉条件。现在怎么确定carry数组呢?


我们设置第一位为front,最后一位为rear。由于flip(a)是a的翻转数,计算过程的第i位和第front+rear-i位的两位相加的数字是相同的。

所以,我们可以根据n在这两个位置的数字的不同,决定它们的carry值。

首先我们可以确定carry[front - 1]和carry[rear]均为0,这是初始条件。

现在设置两个变量,l = front , r = rear,并且让l递增,r递减,然后根据已知的carry[l-1],carry[r]以及n的第l和r位数字,确定出carry[l]和carry[r-1],明显这是一个递推关系。

确定方法如下,其中s[i]位n的第i位数字,a[i],b[i]分别为a和flip(a)的第i位数字:

因为s[l] =a[l] + b[l] + carry[l],s[r] = a[r] + b[r] + carry[r],并且a[l] = b[r],a[r] = b[l]

所以如果s[l] 等于 s[r],那么carry[l]必定等于carry[r],如果s[l] 与 s[r] 相差1,那么carry[l]与carry[r]也必定相差1,如果s[l]与s[r]相差大于1(0和9的话需要特判),那么肯定无解.

另外注意一下,l 和 r-1相等的时候,如果求出的carry[l] 和 carry[r-1] 不相等,那么肯定无解.

具体的判断方式用代码和注释表示会更清晰:

                for(int l = front; (l<<1) < front + rear; ++l){ //(l<<1)就是l+l,退出循环的条件是l>=r                        int r = front + rear - l;               //求出r,注意,此时carry[l-1]以及之前的值,carry[r]以及之后的值都是已知的,.                                                                        //需要求的是carry[l] 和 carry[r-1].                        if(s.charAt(l) == s.charAt(r)){                                carry[l] = carry[r];   //如果两位数字相等,说明这两位的加数和进位均相同,直接令carry[l] = carry[r].                                carry[r - 1] = carry[l - 1];    //同样的,r的进位和l的进位也必定相同,即carry[r-1] = carry[l-1].                                if(l == r - 1 && carry[l - 1] != carry[r]) //如果l==r-1,但carry[l]与carry[r-1]不同,则直接返回false,但是不能直接比较carry[l]与carry[r-1],                                        return false;     //因为这时他们是同一个数,只能比较他们本应被赋的值,即carry[l] 用 carry[r]代替,carry[r-1] 用carry[l-1]代替.                        }                        else if(s.charAt(l) == s.charAt(r) + 1){                                if(carry[r])                                        return false; //如果s[l] == s[r] + 1,那么carry[r]应该为0,carry[l]应该为1,所以如果carry[r]为1,那么必定无解.                                if(l == r -1 && !carry[l - 1])     //l等于r-1时,仍然比较carry[l] 与 carry[r-1] 本应被赋的值,因为carry[l]本应等于1,所以,carry[l-1]也应该等于1,否则无解.                                        return false;                                 carry[l] = true;                                carry[r - 1] = carry[l - 1]; //因为若满足s[l] == s[r] + 1,则s[r]最大值为8,所以,不存在l位进位,r位不进位的情况,即直接令其相等.                        }                        else if(s.charAt(l) + 1 == s.charAt(r)){                                  //与上一个判定类似,可以自己思考.                                if(!carry[r])                                        return false;                                if(l == r -1 && carry[l - 1])                                        return false;                                carry[l] = false;                                carry[r - 1] = carry[l -1];                        }                        else if(s.charAt(l) == '0' && s.charAt(r) == '9'){                                //这个是s[l] == s[r]+1的一种特殊情况,与其类似,需要注意的是因为carry[r]为0,两个一位数相加时,如果没有来自前一位的进位,                                //不可能加出来19,所以当s[r]等于9时,carry[r-1]必定为0.又因为carry[l]必定为1,两者注定不相等,所以如果l == r-1,则必定矛盾,即无解.                                if(carry[r])                                        return false;                                if(l == r - 1)                                         return false;                                carry[l] =  true;                                carry[r - 1] = false;                        }                        else if(s.charAt(l) == '9' && s.charAt(r) == '0'){                                //与上一个判定类似,可以自己思考下.                                if(!carry[r])                                        return false;                                if(l == r -1)                                        return false;                                carry[l] = false;                                carry[r - 1] = true;                        }                        else{                                //如果相差大于1,则无解.                                return false;                        }                }
当构造出carry,要判定一下,即若n的第i位为0,并且当前位含有来自i+1的进位,那么该位肯定会往前进位,即carry[i-1] 必须为 1,否则即为无解.

同样的,当n的第i位为9,并且由往前进位了,那么这一位肯定有来自下一位的进位,即carry[i]必须为1,否则即为无解.反映到代码中就是:

                for(int l = front; l <= rear; ++l){                        if(s.charAt(l) == '0' && carry[l] && !carry[l - 1] ){                                return false;                        }                        if(s.charAt(l) == '9' && carry[l-1] && !carry[l]){                                return false;                        }                }

最后就是根据carry数组,构造出a的每一位数字,这个应该很容易想,设s[i] = x,如果这一位往前进位了(即carry[i-1]为1),则x应该加10,如果这一位有来自前一位的进位(即carry[i]=1),那么x应该减去1,最后得到的x就是a[i]与b[i]的和,也可以理解为a[l]与a[r]的和,为了使a不含前导零,就让a[l]取较大的值.还有一点是如果求出的第一位是0的话也是算无解的,因为a不允许由前导0,代码如下:

                int[] bit = new int[n + 10];                for(int l = front; (l<<1) <= front + rear; ++l){                        int x = s.charAt(l) - '0';                        int r = front + rear - l;                        x +=  (carry[l-1] ? 10 : 0) - (carry[l] ? 1 : 0);                         if(l == r && (x&1) == 1)                                return false;        //如果l==r,说明这一位相加的两个数字是a的最中间的那一位数字,所以只有x是偶数时才可以,否则就为无解                        bit[r] = (x >> 1);                        bit[l] =  x - bit[r];                }                if(bit[front] == 0)                        return false;

这样的话,就将解构造出来,另外还有一个非常关键的一种情况,就是当n的第一位为1时,有可能a的位数比n的位数少一位(例如a=56,n=121),这个时候只要将front置为第二位,并令carry[front-1] = 1 (想一想为什么) 进行构造即可.

AC代码如下,这里面由这样几个细节,首先,carry只由1和0组成,用boolean类型即可,其次,为了使carry[frong-1]不越下届,预处理时,在s的首位添加了一个0.注释部分,我保留了的测试用的代码,就是这些代码帮我找到了很多我没考虑到的细节.有兴趣的可以研究下.

import java.util.Arrays;import java.util.Scanner; public class ProblemD {        private static String ans;        private static void pretreat(StringBuilder s){                for(int i = 0; i < s.length(); ++i){                        if(s.charAt(i)  != '0'){                                s = s.delete(0, i);                                break;                        }                }                s.insert(0,'0');//使front-1不小于0        }        private static void solve(StringBuilder s){                pretreat(s);                 if(caseOne(s)){//先判断下第一位为1的情况,a比n少一位是否存在满足条件的a.                        return;                }                else if(caseOthers(s)){//普通情况的判定                        return;                }                else{                        ans = "0";                }        }        private static boolean caseOne(StringBuilder s){                if(s.charAt(1) == '1'){                        return constructAns(s,true);                }                return false;        }        private static boolean caseOthers(StringBuilder s){                return constructAns(s,false);        }        private static boolean constructAns(StringBuilder s,boolean FirstBitHasCarry)        {                int n = s.length();                int front = FirstBitHasCarry ? 2 : 1;                //根据调用者传递的参数,决定front的值,下面的carry初值类似.                int rear = n - 1;                boolean[] carry =new boolean[n+10];                Arrays.fill(carry, false);                carry[front - 1] = FirstBitHasCarry;                for(int l = front; (l<<1) < front + rear; ++l){                        int r = front + rear - l;                        if(s.charAt(l) == s.charAt(r)){                                carry[l] = carry[r];                                 carry[r - 1] = carry[l - 1];                                if(l == r - 1 && carry[l - 1] != carry[r])                                         return false;                        }                        else if(s.charAt(l) == s.charAt(r) + 1){                                if(carry[r])                                        return false;                                if(l == r -1 && !carry[l - 1])                                        return false;                                 carry[l] = true;                                carry[r - 1] = carry[l - 1];                        }                        else if(s.charAt(l) + 1 == s.charAt(r)){                                if(!carry[r])                                        return false;                                if(l == r -1 && carry[l - 1])                                        return false;                                carry[l] = false;                                carry[r - 1] = carry[l -1];                        }                        else if(s.charAt(l) == '0' && s.charAt(r) == '9'){                                if(carry[r])                                        return false;                                if(l == r - 1)                                         return false;                                carry[l] =  true;                                carry[r - 1] = false;                        }                        else if(s.charAt(l) == '9' && s.charAt(r) == '0'){                                //与上一个判定类似,可以自己思考下.                                if(!carry[r])                                        return false;                                if(l == r -1)                                        return false;                                carry[l] = false;                                carry[r - 1] = true;                        }                        else{                                //如果相差大于1,则无解.                                return false;                        }                }                //code below is to check                for(int l = front; l <= rear; ++l){                        if(s.charAt(l) == '0' && carry[l] && !carry[l - 1] ){                                return false;                        }                        if(s.charAt(l) == '9' && carry[l-1] && !carry[l]){                                return false;                        }                }                //code below is to construct the ans;                int[] bit = new int[n + 10];                for(int l = front; (l<<1) <= front + rear; ++l){                        int x = s.charAt(l) - '0';                        int r = front + rear - l;                        x +=  (carry[l-1] ? 10 : 0) - (carry[l] ? 1 : 0);                         if(l == r && (x&1) == 1)                                return false;                        bit[r] = (x >> 1);                        bit[l] =  x - bit[r];                }                if(bit[front] == 0)                        return false;                StringBuilder sb = new StringBuilder("");                for(int i = front; i <= rear; ++i){                        sb.append(Integer.toString(bit[i]));                }                ans = sb.toString();                return true;        }        public static void main(String[] args) {                // TODO Auto-generated method stub               // test();                Scanner scan = new Scanner(System.in);                StringBuilder s = new StringBuilder(scan.nextLine());                solve(s);                System.out.print(ans);               scan.close();        }        /*          //code below is just for test        private static void test(){                  for(int i  = 1 ; i < 1000000; ++i){                                StringBuilder s = new StringBuilder(Integer.toString(i));                                solve(s);                                int x =  myParseInt(ans);                                if(x !=0 && x+ flip(x)!=i){                                        System.out.println(i);                                        System.out.println(ans);                                }                  }                  System.out.println("done");        }        private static int flip(int x){                int ans = 0;                while(x>0){                        ans = ans * 10 + x % 10;                        x /= 10;                }                return ans;        }        private static int myParseInt(String s){                int ans = 0;                for(int i = 0; i < s.length(); ++i){                        ans = ans*10 + (s.charAt(i) - '0');                }                return ans;        }         */}
欢迎各种批评与讨论~!

0 0