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; } */}欢迎各种批评与讨论~!
- Codeforces 625 D Finals in arithmetic
- Codeforces 625D ---- Finals in arithmetic
- Codeforces 625D Finals in arithmetic(构造)
- CodeForces 625 D.Finals in arithmetic(构造)
- codeforces 625-D. Finals in arithmetic(构造+模拟)
- Codeforces 625D Finals in arithmetic(Codeforces Round #342 (Div. 2) D) 题解
- Codeforces #342 D. Finals in arithmetic 模拟 构造
- cf#342-D - Finals in arithmetic-构造
- 构造 D - Finals in arithmetic ★ ★
- Codeforces 629 D Finals in arithmetic(最大上升子序列和,O(nlogn)、线段树/树状数组)
- Finals in arithmetic 分类讨论
- CF625D-Finals in arithmetic-构造
- 【Codeforces】【Coder-Strike 2014 - Finals】【D Bug in Code】
- codeforces 604 D. Moodular Arithmetic
- 【codeforces 604D】Moodular Arithmetic
- codeforces 710D Two Arithmetic Progressions
- CodeForces 710D Two Arithmetic Progressions
- CodeForces 710D Two Arithmetic Progressions(数学题)
- bzoj2882 工艺
- poj 1716(贪心)
- usaco training 1.3奶牛回文
- CSS基础研究(一)-各种各样的选择器
- JAVA安装——Windows
- Codeforces 625D ---- Finals in arithmetic
- git常用命令速查表
- abstract class和interface的使用场景分别是什么?
- Android特效专辑(九)——仿微信雷达搜索好友特效,逻辑清晰实现简单
- A Java RunTime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Ecl
- bzoj1398 寻找主人
- 网络
- KETTLE——(二)数据抽取
- iOS --- 如何隐藏APP的statusBar